mujoco-react uses a layered provider pattern. Two setup options:MujocoCanvas wraps R3F <Canvas> and forwards all Canvas props. MujocoPhysics provides just the physics context inside your own Canvas.
The outermost wrapper. Loads the @mujoco/mujoco WASM module and provides it to all children. Must wrap your entire app (or at least the part that uses mujoco-react).
A thin wrapper around R3F’s <Canvas>. It accepts a SceneConfig and all standard Canvas props (camera, shadows, style, etc.). Internally creates a MujocoSimProvider that loads the model and starts the physics loop.
For use inside your own <Canvas>. Accepts the same physics props as MujocoCanvas (config, paused, speed, gravity, etc.) but doesn’t create a Canvas. This gives you control over gl settings, post-processing pipelines, and R3F context composition.
Controllers are React components that call useBeforePhysicsStep to write data.ctrl. The library ships useIkController() as one example; the same pattern works for any control logic.
// This is a complete controller:import { ModelActuators, useBeforePhysicsStep, useCtrl } from "mujoco-react";function MyController() { const shoulder = useCtrl(ModelActuators.franka.actuator1); useBeforePhysicsStep(({ data }) => { shoulder.write(Math.sin(data.time)); }); return null;}
See Building Controllers for full patterns including custom IK solvers, config-driven controllers, and the createController factory.
Physics stepping is decoupled from the render frame rate. Each render frame, the provider advances simulation time to match wall-clock time (scaled by speed). If the browser drops frames, multiple mj_step calls run in a single render frame to catch up.
// Pseudocode of the stepping loopwhile (simTime < wallTime * speed) { mj_step(model, data); simTime += timestep;}
All visual components accept standard R3F group props (position, rotation, scale, visible, etc.), so you can position and transform them like any Three.js group.