Skip to content

4.0.0

Choose a tag to compare

@mmossg mmossg released this 06 Nov 15:39

Version 4.0.0 (November 6, 2026)

Important

Read the MuJoCo 4.0 announcement for an overview of everything new since 3.0, the interactive showcase at mujoco.org, MuJoCo Live browser demos, visual showcases, and maintainer Q&A.

Significant new features

  1. 89daf8d19 Added the speculative enable flag for speculative contacts to prevent fast-moving convex geoms from tunneling through thin obstacles within a single
    timestep. On the right, one step of conservative advancement between two rotating boxes. See
    model/speculative/ for examples.

  1. Added elasto-plasticity, with stateful plastic deformation stored in mjData.plastic (mjSTATE_PLASTIC):


  1. abaf8d518 Split the Python distribution into mujoco-core (minimal bindings and C library depending only
    on numpy), mujoco-studio (Studio and Filament renderer bindings), and mujoco (default package installing
    both). When upgrading from a pre-split mujoco installation with pip, uninstall first
    (pip uninstall -y mujoco && pip install mujoco) or run
    pip install --force-reinstall --no-deps mujoco mujoco-core mujoco-studio after upgrading so pip does not
    remove files moved to mujoco-core. To switch an existing mujoco installation to core-only, run
    pip uninstall -y mujoco mujoco-studio && pip install --force-reinstall --no-deps mujoco-core.

Note

Updated documentation for the split Python packages (mujoco, mujoco-core, mujoco-studio) and prebuilt OpenUSD wheels (mujoco-usd / mujoco[usd]) landed shortly after the 4.0.0 release (39ddbb61a). Please refer to the latest docs for Python bindings and OpenUSD rather than stable or 4.0.0.

Engine

Warning

Breaking changes

  1. a65050166 Changed the semantics of sidesite: the tendon now goes around
    the geom the same way as the path from the first endpoint, through the side site, to the second endpoint (thin
    grey line below). Formally, the tendon is the shortest path homotopic to this polyline in the wrapping plane with
    the geom removed.

Previously, a half-plane test was used, which could make the tendon jump onto the geom, a discontinuity in tendon
length, when the segment between the endpoints moved away from the side site while clear of the geom. It could
also select the wrong side for wraps longer than 180 degrees. Top row: old rule; bottom row: new rule.

The new rule has a single discontinuity: when an endpoint crosses the ray opposite the side site, the path
through the side site changes its winding around the geom and the tendon snaps off. This can change the behavior
of existing models.

Migration: Place side sites so that the endpoints do not cross this ray over the range of motion, typically
near the middle of the wrapped arc. For example, the side sites of the
two-link arm were moved from
directly below the joints to 45 degrees below the arm, which reproduces the previous tendon lengths exactly.

  1. 9a7f240f0 The RK4 integrator is now fourth-order accurate for the
    orientations of ball and free joints. Previously, the angular velocities of the Runge-Kutta stages were combined
    without the Runge-Kutta-Munthe-Kaas correction, so orientations converged only at second order. This changes the
    simulation of models with ball or free joints that use RK4.

  2. 9916cc5f7 Added surfacevel[3] to mjContact, storing the relative
    surface velocity (geom[1] minus geom[0]) in the contact frame
    (tangent1, tangent2, spin). Collision detection populates it from geom_surfacevel when
    mjModel.flg_surfacevel is set, and external contact generators can write it directly (setting
    flg_surfacevel = 1 if no geom specifies surfacevel). Contact-point visualization now draws a single relative
    tangential velocity arrow per active frictional contact.

  3. 629feb74b Added xiquat, geom_xquat, and site_xquat to mjData, storing the Cartesian
    quaternion orientations of body inertias, geoms, and sites alongside ximat, geom_xmat, and site_xmat.

  4. 64af6504f Added mjModel.tendon_type with values from the new mjtTendon enum, distinguishing
    fixed and spatial tendons. Previously, the type was implied by whether the first path element was a joint.

Deformables

Warning

Breaking changes

  1. 1d6cc8ff5 Removed the cable plugin (plugin/elasticity) and the composite element, which are
    superseded by 1D flexcomp with elasticity.

    Migration:

    • count and curve / size / vertex: use flexcomp dim="1" with
      type="grid", count, and spacing (or type="circle" for closed loops), or type="direct" with
      point. Add <edge equality="true"/> to keep edges inextensible.

    • Geom size (capsule radius): set flexcomp radius. For geom type="box", set
      <elasticity section="box" sectionsize="..."/>.

    • Plugin bend and twist: set <elasticity young="..." shear="..."/>.

    • Root attachment initial: initial="none" maps to <pin id="0 1"/> (clamps the first edge's material
      frame to the parent body and omits <name>_twist_0); initial="ball" maps to <pin id="0"/> (spherical
      anchor at the first vertex, keeping <name>_twist_0 free); initial="free" maps to no <pin/>.

    • Plugin flat="true": define the rod straight in qpos0 (e.g., type="grid") and set the curved initial
      pose via qpos or a keyframe (curves defined directly in point are at rest in qpos0,
      corresponding to flat="false").

    • Plugin vmax stress coloring: not supported.

    • Integrator: use integrator="discrete" for implicit integration (or Euler / RK4; implicit and
      implicitfast are not supported for 1D rod elasticity).

    • Body names B_first / B_<i> / B_last: unpinned vertices create bodies <name>_<i> (pinned
      vertices attach directly to the parent body), plus per-edge twist bodies and joints (see the next entry).

  2. 95cf15e58 Open elastic 1D flexcomps with at least two elements now create
    per-edge twist bodies with hinge joints <name>_twist_0, ..., <name>_twist under the world body (omitting
    the joint of an edge clamped by pinning both of its points: <name>_twist_0 for <pin id="0 1"/>, and
    <name>_twist for the last two points of a rod with at least three elements), and store their time-parallel
    reference frames and reference twists in mjData.plastic. This changes nq, nv, nbody, nplastic
    and the qpos layout of such models, shifts the indices of bodies and joints declared after the flexcomp, and
    changes the simulation: rods now roll on surfaces and twist in grippers. Box sections (section box)
    require these joints, so raw flexes with box sections now need them too, and their
    collision radius is set to the minor half-size of the section (an explicit radius that differs from
    it is now a compile error).

    Migration: Update keyframes and any code that indexes qpos, qvel or bodies by number, preferably
    addressing vertex bodies (<name>_<i>) and twist joints by name. Save and restore rod state with
    mjSTATE_PLASTIC. Raw box-section flexes need hinge joints named as above (see
    elasticity).

  3. 7ba52b533 Fixed the elastic constants of non-interpolated flexes (full and
    radial dofs), which now match young,
    poisson and thickness as documented, and the
    interpolated flexes:

    • 3D Saint Venant-Kirchhoff stretching used twice the first Lamé parameter $\lambda$.

    • 2D stretching used a quarter of the shear modulus $\mu$ and half of the 3D $\lambda$, instead of
      $\mu$ and the plane-stress $\lambda^* = E \nu / (1 - \nu^2)$.

    • 2D bending used $\tfrac{3}{4}(1-\nu) D$ instead of the plate bending rigidity
      $D = E t^3 / (12 (1 - \nu^2))$.

    Models with these flexes become stiffer, except 3D models with zero Poisson's ratio, which are unchanged.

    Migration: To keep the previous behavior, use these parameters:

    • 3D: $\nu' = 2\nu / (1 + 2\nu)$ and $E' = E (1 + \nu') / (1 + \nu)$.

    • 2D bending only (elastic2d="bend"): $E' = \tfrac{3}{4}(1 - \nu) E$.

    • 2D stretching (elastic2d="stretch" or "both"): $\nu' = 2\nu$, which is exact for
      $\nu &lt; 1/4$ (so that $\nu' &lt; 1/2$). Then:

      • "stretch": $E' = E (1 + 2\nu) / (4 (1 + \nu))$.

      • "both": $t' = t \sqrt{3 (1 - 2\nu)}$ and $E' = E t (1 + 2\nu) / (4 (1 + \nu) t')$.

    The flex models in model/flex were remapped in this way, so their behavior is unchanged.

  4. c0e6b15fd 1D rod bending elasticity (dim="1" with positive young) is no
    longer supported under implicit and implicitfast, matching 2D and 3D flex elasticity.

    Migration: Use integrator="discrete" for implicit integration, or integrator="Euler" with a small
    timestep for explicit integration.

  5. c0e6b15fd Flex edge stiffness and damping are no
    longer supported under integrator="discrete" (where they were integrated explicitly rather than in the
    effective metric).

    Migration: Use elasticity (or edge equality), or use
    Euler/RK4/implicitfast.

  6. b2577a630 Unpinned flexcomp models (with full, trilinear, or
    quadratic dofs) now fail to compile if their parent or ancestor body has joints
    when no active vertices or nodes are pinned to it.

    Migration: Remove joints from the parent/ancestor body of an unpinned flex, or pin at least one vertex/node to
    the parent body with pin.

  7. 145cdc2be Removed flexvert equality constraints (<equality><flexvert> and
    <edge equality="vert"/> in flexcomp), along with the associated mjModel fields
    (nJfv, flex_vertedgeadr, flex_vertedgenum, flex_vertedge, flex_vertmetric,
    flexvert_J_rownnz, flexvert_J_rowadr, flexvert_J_colind) and mjData fields
    (flexvert_length, flexvert_J). mjEQ_FLEXVERT is retained as an unsupported placeholder in
    mjtEq and causes an error if used.

    Migration: Use continuum elasticity with integrator="discrete" or
    edge equality for 2D cloth models.

  8. 6f28d4adf Flexes now support per-element visual materials: mjModel.flex_matid is now
    (nflexelem x 1) instead of (nflex x 1), and the material attribute on flex
    and flexcomp (mjsFlex.material) is now mjStringVec rather than mjString
    (accepting either a single material name broadcast to all elements or one material name per element).

    Migration: Index mjModel.flex_matid by element m->flex_elemadr[f] + e rather than by flex f. In C,
    set mjsFlex.material with mjs_setStringVec (or mjs_appendString) instead of mjs_setString; in
    Python, assign a list of material names (e.g. flex.material = ['mat']).

  9. 7ba5d6732 Records of 1D flexes in mjModel.flex_bending grow from 4 to 10 values, to store curved
    reference configurations and pairs of adjacent bent vertices. Previously, each record was
    [v_prev, v, v_next, k].

    Migration: Read 1D records with stride 10. A record whose fourth value is negative is a single hinge
    [v0, v1, v2, -1, k1, k2 - k1, |kb0|, jnt, kt, b]: principal bending stiffnesses k1 <= k2, the twist hinge
    joint jnt of the edge (v1, v2) (-1 if none; the joint of the first edge, when present, is that of the
    first record minus one), torsional stiffness kt, and the signed minor half-size b of a box section (0
    for circular sections). Otherwise it is a hinge pair [v0, v1, v2, v3, w0, w1, om0[4]].

  1. 8892bf44b, 0d8a109de, f9a589361 Sparse Newton (solver) is
    1.4-3x faster on flex models, using fill-reducing DOF ordering, incremental active-set
    Hessian updates, direct elliptic cone folding, and pre-ordered sparse Cholesky for the 2D
    discrete integrator smooth solve. Most flex models in model/flex now use
    solver="Newton", and the experimental ipc flag now supports solver="Newton" in
    addition to solver="CG".

  2. 57d9b960f solver="Newton" with the discrete integrator now supports
    flex vertices and nodes (both standard and interpolated) attached to articulated bodies.
    Previously, such models required solver="CG". The model/flex/gripper_trilinear.xml gripper now uses
    solver="Newton". MuJoCo Warp still requires solver="CG" for such models.

  3. 7ba5d6732 1D rod bending elasticity now supports curved reference configurations
    and torsional stiffness: vertices bent by more than $10^{-4}$ rad in qpos0 keep their reference curvature
    and dihedral twist angle, so that rods such as rings, arcs, and coil springs are at rest in their compiled shape.
    Previously, such models failed to compile. The new shear attribute optionally
    overrides the shear modulus used for torsion. Open rods carry per-edge twist joints (see the breaking change above)
    with per-edge polar inertia, frame-indifferent relative Rayleigh torsional damping, time-parallel reference frames,
    and frictional contact lever-arm coupling. Box sections additionally use anisotropic principal stiffnesses with
    equalized damping ratio ($\sqrt{k_1 k_2}$ on the weak axis) and an oriented box in the DER material frame for
    rendering and collision detection. See the documentation.

  4. 9ca6b7171 Volumetric interpolated flexes (trilinear or quadratic dofs
    with elastic2d="none") now integrate cell stiffness and lump nodal masses over the mesh volume using Finite Cell
    Method (FCM) subcell quadrature instead of treating every non-empty grid cell as a full cube. This changes the
    stiffness and nodal mass distribution of volumetric interpolated flexes whose mesh does not fill its bounding box,
    as well as the nodal mass distribution of multi-cell trilinear flexes.

  5. f9f3ee653 Contacts with flex elements now distribute the contact to the element vertices with the
    barycentric coordinates of the point on the element closest to the contact. Previously the vertex weights were
    inversely proportional to the distances from the contact, which do not interpolate the contact point: the contact
    Jacobian measured the velocity of a different material point, and the contact force exerted a spurious torque on the
    element. This changes the simulation of models with flex contacts.

  6. 115907e6d Interpolated flexes (trilinear or quadratic dofs) now support
    passive contact.

  7. 51572eab0 Flex edge damping is now integrated explicitly under implicit and
    implicitfast (previously it also damped rigid translation).

  8. 9fb4eca48 Fixed contact Jacobians of quadratic flexes, which dropped nodes with negative basis weights.

  9. b16e09838 Sphere and capsule contacts with 3D flex elements are now computed analytically instead of the
    general convex pipeline.

  10. 3bbf510d4 Improved numerical stability of nativeccd convex collision
    detection and multiccd contact generation. This can slightly change contacts for models
    with convex or mesh collisions.

Compiler

Warning

Breaking changes

  1. 00057bcef Models that include the same file through different paths, which previously
    loaded, are now rejected with already included.

    Migration: Remove redundant <include> elements referencing the same file.

  2. The compiler now rejects the following invalid models, which previously loaded:

    Migration: Ensure height fields have nrow >= 2 and ncol >= 2, fixed tendons wrap distinct slide
    or hinge joints with non-negative armature, pulley divisors are positive, cameras referenced by
    camprojection or rangefinder sensors have positive resolution, skin face indices are in [0, nvert),
    and cone meshes have at least 3 edges and a radius in [0, 1].

  1. 432376a59 fullinertia and inertias inferred from geoms which are not
    positive definite are now bounded by boundinertia when it is positive. Previously they
    gave a compile error.

  2. a4c699040 mj_encode now supports binary MJB output for mjSpec via the
    model/vnd.mujoco.mjb content type.

Rendering

  1. 1edd2c67d Added a castshadow attribute to geom
    (mjsGeom.castshadow, mjModel.geom_castshadow, mjvGeom.castshadow; default true) to disable shadow
    casting per geom in both the classic OpenGL and Filament renderers, and mjrf_setRenderableCastShadow
    (mjrf.Renderable.set_cast_shadow in Python) to toggle shadow casting of Filament renderables dynamically.

  2. c831b97d6 Added horizon haze and skybox visibility support to Filament rendering: infinite planes are
    centered beneath the camera and blend smoothly into the skybox horizon according to
    visual/map/haze and visual/rgba/haze, toggled per view via haze
    and skybox on mjrfRenderRequest (mjRND_HAZE and mjRND_SKYBOX).

  3. 939bc882e When no light specifies an intensity, the fallback intensity given
    to each light in Filament rendering is now scaled by the brightness of its diffuse color, which is factored out of
    the light's color. Lights look the same, but the brightest directional light now casts the shadows, rather than an
    arbitrary one that could differ between runs.

  4. 0fc0b44d6 Added filament.material.base_color_type flag for configuring how the base color
    material parameter is interpreted.

  5. e19117325 Added mjrf_flush (mjrf.Context.flush in Python) to flush pending operations to the
    rendering backend and invoke any completed callbacks.

  6. 93033f5ba Added mjRND_POST_PROCESSING rendering flag.

  7. e66b53380 Allow headlight.active to be set to "false", "true", or "auto"
    while continuing to support integer values (0=false, 1=true, 2=auto). Change the default to "auto" instead of
    "true".

Python bindings

  1. 68390c316 Removed support for Python 3.10, now that it's
    deprecated upstream.

  2. a1b9fce44 Bundled the MuJoCo Sphinx manual (doc/**/*.rst) and agent skills (doc/skills/**/*.md)
    inside the mujoco package under mujoco/doc/.

Samples

Warning

Breaking changes

  1. 006586d30 Removed sample/record.cc; multi-frame raw RGB24 video recording is now supported by
    sample/render.cc across both the Filament and classic OpenGL backends via --duration and
    --fps.

    Migration: Replace record model.xml T F out.rgb 0 with
    render model.xml out.rgb --duration=T --fps=F (or pass - as the output path to pipe top-down RGB24 frames
    directly to ffmpeg; vflip is no longer needed).

Models

  1. 9647e0290 Added six
    elasto-plasticity example models, each
    placing an elastic reference next to a plastic counterpart: joint and tendon springs, 2D shells and 3D continua.

  2. 89daf8d19 Added
    speculative contacts example models: a
    Newton's cradle of thin plates, an interlocking chain, and a ball struck by a hinged flipper.

Documentation

  1. 74ad32d20 Added documentation for MuJoCo Studio, covering web
    and native viewers, architecture, and
    Python and C++ development.

  2. 0f1092ebb Added a Model tuning guide to the Modeling chapter---covering the
    fidelity--speed trade-off, tuning defaults, priority-ordered global
    and model-level parameters, deformables, and
    system identification---along with module documentation for mujoco.sysid.

Bug fixes

  1. fdf141238 accelerometer and framelinacc sensors
    and mj_objectAcceleration on static and mocap bodies now report the gravitational proper acceleration, like a
    dynamic body at rest. Previously they returned zero, a regression introduced in 3.3.7.

  2. c4137ef7c, aeb871ba1 Actuators saturated by joint or tendon actuator force limits
    (joint and tendon
    actuatorfrcrange) no longer contribute implicit actuator derivatives, changing integration under implicit,
    implicitfast, and discrete. Because saturated actuators no longer contribute implicit damping, models with
    light links and no armature may need joint armature or a smaller timestep.

  3. aaf2abaa7, 1b46a496e Fixed sleep filtering bugs in history sensors
    (nsample, delay, interval), mj_energyPos, flex passive forces, tendon limit and
    frictionloss constraints, two-tree tendon armature and bias, mj_subtreeVel, and
    caller-supplied mj_rne buffers.

  4. 13fb7844e mj_setConst now wakes all sleeping trees, since it recomputes their
    kinematics at qpos0 and qpos_spring. Previously their cached poses could be left at qpos_spring. It also
    no longer overwrites cam_xpos, cam_xmat, light_xpos and light_xdir in mjData, which left
    static cameras and lights with tracking or targeting at their fixed-mode poses.

  5. 432376a59 Contact pairs created with an undefined mjsPair.solref now mix the solref of their geoms
    using solmix when both are positive, like contacts not defined by pairs. Previously they always took the
    element-wise minimum.

  6. 18d65f4e0, ea6aa8214 Filament depth and segmentation renders no longer apply post-processing
    (tone mapping, dithering), which corrupted depth values and segment ID colors; perspective depth renders now project
    with the finite zfar plane instead of Filament's infinite far plane, matching orthographic
    depth and the classic renderer; and the mjr_ compatibility API now respects readDepthMap
    (mjtDepthMap) on mjrContext in mjr_readPixels and colors geoms by segid + 1 like
    mjRND_IDCOLOR so a geom with segid 0 no longer renders as background.

  7. 9b19fd07c, ef34acbee File paths on Windows are now interpreted as UTF-8 rather than the active
    ANSI code page, fixing loading and saving files, plugin library discovery and loading (mj_loadPluginLibrary,
    mj_loadAllPluginLibraries), and simulate executable directory lookup when paths contain
    non-ASCII UTF-8 characters (#3139). Contribution by @mar-yan24.

  8. a5ff8f6c7 Fixed dcmotor parameter handling: allow zero
    motorconst for input="none", reject negative constants (e.g.
    motorconst="-a b" is now rejected instead of silently using K = b), replace capacitance when an inconsistent
    thermal triple is given, clear inherited
    forcerange when saturation is authored, let
    zero saturation or inductance clear class defaults, and validate actdim at
    compile time.

  9. 8694cc9ce Improved numerical stability of sphere and cylinder spatial tendon
    geom wrapping at wrap onset. Thanks to @M-Colley for reporting and analyzing the
    issue in MJWarp issue 1772.

  10. b6192732b mj_ray now scales SDF hit distances by 1 / ||vec|| for non-unit ray vectors, and
    mju_rayTriangle rejects triangle intersections behind the ray origin.

  11. 00c19f04b mj_resetData, mj_copyData, and mj_recompile now preserve the destination
    mjData thread pool instead of dropping and leaking it.

  12. a206daa42 mju_mat2Rot now normalizes the input quaternion and escapes 180-degree stationary points
    (saddles and minima) that do not maximize $\operatorname{tr}(R^\top M)$.

  13. afa16414c Fixed the cotangent weight and length scale of the curved reference term of 2D
    flex bending (elastic2d bend or both), so
    out-of-plane bending forces vanish at curved rest configurations and scale consistently with model size.

  14. a4c699040 Fixed several bugs when editing or copying an mjSpec:

    • mjs_delete (MjSpec.delete in Python) now removes deleted default classes from the class lookup map
      instead of leaving dangling pointers.

    • mj_copyBack (MjSpec.copy_back in Python) preserves material metallic, roughness, and
      texuniform attributes.

    • mjs_findChild no longer matches a non-world body against itself.

    • mjs_setToMuscle accepts NULL timeconst and range pointers.

  15. 00057bcef Fixed several MJCF XML loading and saving bugs:

  16. 881e53e2e Legacy .msh meshes with mirrored scale no longer double-flip face
    winding (which inverted faces), and .msh files without vertex normals no longer populate empty face normals or
    reject custom normals on recompilation.


Update (November 7, 2026, 01:00 PST): The four standalone release archives (mujoco-4.0.0-linux-x86_64.tar.gz, mujoco-4.0.0-linux-aarch64.tar.gz, mujoco-4.0.0-windows-x86_64.zip, and mujoco-4.0.0-macos-universal2.dmg) and their .sha256 checksum files were rebuilt and replaced to remove usd_decoder_plugin from the simulate auto-load plugin directory, fixing a startup crash when Python/OpenUSD runtime libraries are not installed. The 4.0.0 Git tag, PyPI wheels, and npm WASM package are unchanged.