Skip to main content

Provider Hierarchy

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.

MujocoProvider

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).

MujocoCanvas

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.

MujocoPhysics

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.

MujocoSimProvider (internal)

You don’t use this directly. MujocoCanvas and MujocoPhysics create it. It:
  • Loads the model from SceneConfig
  • Runs the physics loop via useFrame at priority -1
  • Exposes the MujocoSimAPI via React context
  • Provides callback registration for useBeforePhysicsStep, useAfterPhysicsStep, and resetCallbacks

Controller Plugins

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.
See Building Controllers for full patterns including custom IK solvers, config-driven controllers, and the createController factory.

Physics Loop

The physics loop runs inside useFrame at priority -1, ensuring it executes before any rendering:
Note that IK solving happens inside useBeforePhysicsStep (registered by useIkController), while gizmo animation runs at default priority via useFrame.

Timing

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.

State Management

All mutable simulation state lives in refs, not React state. React state updates trigger re-renders, and at 60fps that kills performance.
Hooks like useBodyState and useJointState return refs that update every frame without re-rendering:

Composability

Everything inside <MujocoCanvas> (or <MujocoPhysics>) is a standard R3F child. You mix library components with your own:
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.

Accessing the API

Two ways to access the simulation API:

1. Ref (outside R3F)

2. useMujoco() hook (inside R3F)

useMujoco() can only be called from components that are children of MujocoCanvas or MujocoPhysics. Calling it outside will throw an error.