dm_control: DeepMind's Python Layer Over MuJoCo, and What It Costs to Keep It Current
Google DeepMind's software stack for physics-based simulation and Reinforcement Learning environments, using MuJoCo.
At a glance
- What is it?
- dm_control packages MuJoCo physics bindings, a standard continuous-control suite, an MJCF editing library and an interactive viewer behind one pip install. The catch is a release cadence that tracks MuJoCo point versions, a package that refuses editable installs, and rendering that depends on which OpenGL backend your machine can actually provide.
- Who is it for?
- Adopt dm_control if you want the DeepMind continuous-control task definitions and the MJCF composition API in the same install as the MuJoCo bindings, and if pinning the package version alongside a matching MuJoCo release fits your workflow. Do not adopt it if you only need raw simulation, because the mujoco package already exposes the pybind11 bindings and skips the legacy header-generated code that breaks editable installs.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap dm_control fills between a physics engine and a research task
MuJoCo is a physics engine. It simulates contacts, joints and actuators, and it does not decide what a reinforcement learning agent should be rewarded for. dm_control is the layer that supplies those decisions, plus the Python ergonomics around the engine. Its README lists three core components: dm_control.mujoco for Python bindings to the engine, dm_control.suite for a set of RL environments, and dm_control.viewer for interactive inspection. Three further components exist for building custom tasks: dm_control.mjcf for composing and modifying MJCF models in Python, dm_control.composer for assembling environments from reusable components, and dm_control.locomotion with a soccer subpackage for multi-agent tasks.
The audience is narrow but deep. If you are reproducing continuous-control results on standard locomotion and manipulation tasks, the suite saves you from re-deriving reward functions and termination conditions. If you are authoring new task variants, mjcf lets you build and mutate model XML programmatically instead of hand-editing files. The project is not a training framework. There is no algorithm implementation in the component list, so you bring your own learner and use dm_control as the environment side of the loop.
What actually sits inside the package: bindings, suite, mjcf, viewer
The architecture is layered rather than monolithic. At the bottom, dm_control.mujoco wraps the engine. The README notes that the project has been largely updated to use the pybind11-based bindings provided via the mujoco package, but still relies on some legacy components generated from MuJoCo header files. That mixed lineage is the source of the installation constraint described below, and it is the single most useful thing to understand about the codebase before you depend on it.
Above the bindings, dm_control.mjcf is a Python library for composing and modifying MJCF models. This is where programmatic task construction happens: you express model structure in Python, and the library produces the model the engine consumes. dm_control.composer sits at a higher level, defining environments from reusable, self-contained components, which is the intended path when a single suite task is too coarse for your experiment. dm_control.suite provides the ready-made environments, and dm_control.locomotion extends the task space, with locomotion.soccer covering multi-agent soccer scenarios.
Rendering is orthogonal to all of this and is handled through the MuJoCo bindings, which the README says support three OpenGL backends: EGL for headless hardware acceleration, GLFW for windowed hardware acceleration, and OSMesa for pure software rendering. The viewer is the one component with a hard backend requirement.
Getting it running: pip, versioning, and the editable-install trap
Installation is a single command from PyPI: pip install dm_control. If you want an unreleased revision, the README gives pip install git+https://github.com/google-deepmind/dm_control.git. Both are straightforward. The complication is what you cannot do.
The README states plainly that dm_control cannot be installed in editable mode. Attempting pip install -e produces an import error along the lines of ImportError: cannot import name 'constants' from partially initialized module 'dm_control.mujoco.wrapper.mjbindings'. The stated cause is the legacy components generated from MuJoCo header files, which are incompatible with editable mode. The documented remedy is to pip uninstall dm_control and reinstall without the -e flag. For anyone accustomed to editable installs during development, this is a real workflow change, not a footnote: you cannot patch the installed package in place and expect imports to resolve.
Versioning changed shape at 1.0.0, when the project adopted semantic versioning. Before that, releases were numbered 0.0.N, where N was an internal revision counter that incremented at every Git commit. The practical consequence is that pre-1.0 version numbers carry no compatibility signal, while 1.x numbers do.
Choosing a rendering backend with MUJOCO_GL
Rendering configuration is where most first-run failures will occur, and the README is specific about it. By default, dm_control attempts GLFW first, then EGL, then OSMesa. You can override the order by setting the MUJOCO_GL environment variable to "glfw", "egl" or "osmesa". When rendering with EGL, MUJOCO_EGL_DEVICE_ID selects the target GPU.
The dependencies differ per backend. GLFW and GLEW are needed for windowed hardware rendering, installed on Debian and Ubuntu via sudo apt-get install libglfw3 libglew2.0. Headless hardware rendering through EGL requires EXT_platform_device support in the driver, which the README says recent Nvidia drivers provide, plus GLEW via sudo apt-get install libglew2.0. Software rendering needs GLX and OSMesa, available as sudo apt-get install libgl1-mesa-glx libosmesa6.
Two constraints are worth internalising before you build a pipeline around this. First, the README states that at least one of the three backends must be available in order to render through dm_control. Second, dm_control.viewer can only be used with GLFW, and GLFW does not work on headless machines. Those two statements together mean the interactive viewer and headless cluster rendering are mutually exclusive on the same host configuration. If your workflow is inspect locally, train on a headless node, you are maintaining two environments.
macOS users installing via Homebrew get extra instructions: use a Homebrew-installed Python interpreter rather than the system one, and export DYLD_LIBRARY_PATH=$(brew --prefix)/lib:$DYLD_LIBRARY_PATH before running so the GLFW library is found.
The maintenance tax: releases that track MuJoCo point versions
The release history shows a tight coupling to upstream MuJoCo. Version 1.0.46 upgrades to MuJoCo 3.13.0, 1.0.45 upgrades to 3.12.0, and 1.0.44 upgrades to 3.11.0. Three consecutive releases, each defined by a MuJoCo version bump. That cadence is the maintenance cost in concrete form: if you want engine fixes, you take a dm_control release, and if you pin one, you pin the other. A dependency resolver that lets dm_control float while MuJoCo is held, or the reverse, is a configuration you should verify rather than assume.
The editable-install restriction compounds this. Because you cannot install in editable mode, local patching of dm_control internals is not a supported path, so a workaround you apply today has to be reapplied as a patch or a fork on the next upgrade. The legacy header-generated components are the stated reason, and until those are removed, the constraint stands. Nothing in the supplied material indicates a timeline for that removal.
Licensing is Apache-2.0. That is a permissive licence, and it is the same family used across much of the surrounding Python scientific stack. This is not legal advice, and the licence text governs; if you are redistributing dm_control or a modified version, read the Apache-2.0 terms and any attribution requirements yourself. The README also asks that you cite the accompanying publication, tunyasuvunakool2020 in Software Impacts, if you use the package.
When the mujoco package alone is the better choice
The README names the alternative without framing it as one. dm_control has been largely updated to use the pybind11-based bindings provided via the mujoco package. That means the engine bindings are available directly, without dm_control in the dependency graph. The difference in approach is scope: mujoco gives you the simulator and its rendering backends, while dm_control adds the suite environments, the mjcf composition library, composer, the locomotion tasks and the viewer on top.
If your work is custom task design from scratch, or you are building a simulator-adjacent tool rather than an RL environment, the extra layers are weight you carry without using. You also inherit the editable-install restriction and the release coupling to MuJoCo point versions, neither of which comes from the engine bindings themselves. Conversely, if you want the standard continuous-control task definitions or the MJCF Python API, reimplementing them on raw mujoco is duplicated effort.
One more boundary is worth stating. dm_control is an environment and simulation stack. It does not ship a training loop, and nothing in the component list suggests it does. Choosing it does not reduce the amount of learning-algorithm code you write; it reduces the amount of physics and task-definition code you write.
A concrete adoption checklist before you commit
Start by confirming a rendering backend on the actual target host, not your laptop. Run the install, then set MUJOCO_GL explicitly rather than relying on the default GLFW, EGL, OSMesa fallback order, because a silent fallback to software rendering changes throughput characteristics without changing your code. If you need the viewer, you need GLFW and therefore a display; if the host is headless, the viewer is off the table per the README.
Second, decide your version pinning strategy before the first upgrade lands. Given releases defined by MuJoCo version bumps, pin both packages together and treat the pair as one unit. Third, if your team's habit is editable installs for local development, plan for the fact that this package does not support them, and expect the documented import error if someone tries.
None of these are reasons to avoid dm_control. They are the checks that separate a smooth adoption from a debugging session about why dm_control.mujoco.wrapper.mjbindings will not import.
Editorial conclusion
Adopt dm_control if you want the DeepMind continuous-control task definitions and the MJCF composition API in the same install as the MuJoCo bindings, and if pinning the package version alongside a matching MuJoCo release fits your workflow. Do not adopt it if you only need raw simulation, because the mujoco package already exposes the pybind11 bindings and skips the legacy header-generated code that breaks editable installs. Before committing, verify three things on your target machine: that a working backend exists among GLFW, EGL and OSMesa, since the README states at least one is required for any rendering; that dm_control.viewer is usable, which the README limits to GLFW; and that your dependency resolver can hold the dm_control and mujoco versions together, given releases such as 1.0.46 that upgrade MuJoCo to 3.13.0.
Community notes