mujoco-react 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,12 @@
1
+ <p align="center">
2
+ <img src="docs/images/mj2.gif" alt="mujoco-react demo" width="100%" />
3
+ </p>
4
+
1
5
  # mujoco-react
2
6
 
3
- A thin, unopinionated wrapper around [mujoco-js](https://github.com/nicepkg/mujoco-js) composable and extensible via React. Built on [React Three Fiber](https://docs.pmnd.rs/react-three-fiber). Works with **any robot, any scene**.
7
+ Composable [React Three Fiber](https://docs.pmnd.rs/react-three-fiber) wrapper around [mujoco-js](https://www.npmjs.com/package/mujoco-js). Load any MuJoCo model, step physics, render bodies, and write controllers as React components.
4
8
 
9
+ **[Live Demo](https://mujoco-react-example.pages.dev)** | **[Docs](https://dadd.mintlify.app)** | **[Example Source](https://github.com/noah-wardlow/mujoco-react-example)**
5
10
 
6
11
  ## Install
7
12
 
@@ -15,7 +20,6 @@ npm install mujoco-react three @react-three/fiber @react-three/drei
15
20
  import {
16
21
  MujocoProvider,
17
22
  MujocoCanvas,
18
- SceneRenderer,
19
23
  IkController,
20
24
  IkGizmo,
21
25
  } from 'mujoco-react';
@@ -41,7 +45,6 @@ function App() {
41
45
  style={{ width: '100%', height: '100vh' }}
42
46
  >
43
47
  <OrbitControls enableDamping makeDefault />
44
- <SceneRenderer />
45
48
  <IkController config={{ siteName: 'tcp', numJoints: 7 }}>
46
49
  <IkGizmo />
47
50
  </IkController>
@@ -53,49 +56,28 @@ function App() {
53
56
  }
54
57
  ```
55
58
 
56
- ## Architecture
57
-
58
- Two ways to set up your scene:
59
-
60
- ### `<MujocoCanvas>` — Quick Start
59
+ ## Direct WASM Access
61
60
 
62
- Wraps R3F `<Canvas>` for you. Fastest path to a working scene:
63
-
64
- ```
65
- <MujocoProvider> <- WASM module lifecycle
66
- <MujocoCanvas config={...}> <- R3F Canvas + physics context
67
- <SceneRenderer /> <- Syncs MuJoCo bodies to Three.js meshes
68
- <IkController config={..}> <- Opt-in controller plugin
69
- <IkGizmo />
70
- </IkController>
71
- <YourController /> <- Bring your own controller
72
- <YourLights /> <- You compose your own scene
73
- </MujocoCanvas>
74
- </MujocoProvider>
75
- ```
61
+ `useMujoco()` gives you the full typed MuJoCo WASM binary `mj_step`, `mj_forward`, `MjModel`, `MjData`, and everything else:
76
62
 
77
- ### `<MujocoPhysics>` — Bring Your Own Canvas
63
+ ```tsx
64
+ import { useMujoco } from 'mujoco-react';
78
65
 
79
- Use inside your own `<Canvas>` for full control over gl settings, post-processing, and R3F context composition:
66
+ const { mujoco, status } = useMujoco();
80
67
 
81
- ```
82
- <MujocoProvider>
83
- <Canvas shadows camera={...} gl={...}> <- Your Canvas, your settings
84
- <MujocoPhysics config={config}> <- Physics context only
85
- <SceneRenderer />
86
- <YourController />
87
- </MujocoPhysics>
88
- <OrbitControls />
89
- <EffectComposer>...</EffectComposer> <- Post-processing, etc.
90
- </Canvas>
91
- </MujocoProvider>
68
+ if (mujoco) {
69
+ const model = mujoco.MjModel.loadFromXML('/path/to/scene.xml');
70
+ const data = new mujoco.MjData(model);
71
+ mujoco.mj_step(model, data);
72
+ console.log(data.qpos); // joint positions after one step
73
+ }
92
74
  ```
93
75
 
94
- The library provides **only MuJoCo engine concerns**: WASM lifecycle, physics stepping, and body rendering. Controllers (IK, teleoperation, RL policies) are composable plugins you opt into or bring your own.
76
+ Most users won't need this `<MujocoCanvas>` and hooks like `useBeforePhysicsStep` handle the model/data lifecycle for you. But the raw module is always available when you need it.
95
77
 
96
- ## Bring Your Own Controller
78
+ ## Writing a Controller
97
79
 
98
- **Controllers are just React components.** Write a function that calls `useBeforePhysicsStep` to drive `data.ctrl` each frame, return `null`, and drop it into your scene tree. No base class, no registration — just hooks.
80
+ A controller is a React component that calls `useBeforePhysicsStep` to write `data.ctrl` each frame:
99
81
 
100
82
  ```tsx
101
83
  import { useBeforePhysicsStep } from 'mujoco-react';
@@ -107,76 +89,68 @@ function MyController() {
107
89
  });
108
90
  return null;
109
91
  }
92
+ ```
93
+
94
+ Drop it into the tree:
110
95
 
111
- // Drop it in:
96
+ ```tsx
112
97
  <MujocoCanvas config={config}>
113
- <SceneRenderer />
114
98
  <MyController />
115
99
  </MujocoCanvas>
116
100
  ```
117
101
 
118
- This is the primary way to use the library. IK, teleoperation, RL policies, state machines — they're all just components that read input and write to `data.ctrl`.
119
-
120
- ### Bring Your Own IK
121
-
122
- The built-in `<IkController>` uses a generic Damped Least-Squares solver, but you can plug in **any** IK solver — analytical, learned, or from another library:
102
+ The `createController<TConfig>()` factory adds typed config and default merging for reusable plugins:
123
103
 
124
104
  ```tsx
125
- import type { IKSolveFn } from 'mujoco-react';
105
+ import { createController, useBeforePhysicsStep } from 'mujoco-react';
126
106
 
127
- const myIK: IKSolveFn = (pos, quat, currentQ) => {
128
- return myAnalyticalSolver(pos, currentQ); // return joint angles or null
129
- };
107
+ export const MyController = createController<{ gain: number }>(
108
+ { name: 'MyController', defaultConfig: { gain: 1.0 } },
109
+ ({ config }) => {
110
+ useBeforePhysicsStep((_model, data) => {
111
+ data.ctrl[0] = config.gain * Math.sin(data.time);
112
+ });
113
+ return null;
114
+ },
115
+ );
130
116
 
131
- <IkController config={{ siteName: 'tcp', numJoints: 7, ikSolveFn: myIK }}>
132
- <IkGizmo />
133
- </IkController>
117
+ // <MyController config={{ gain: 2.0 }} />
134
118
  ```
135
119
 
136
- Or skip `<IkController>` entirely and solve IK yourself inside `useBeforePhysicsStep`:
120
+ ## Architecture
121
+
122
+ `<MujocoCanvas>` wraps R3F `<Canvas>` and forwards all Canvas props (`camera`, `shadows`, `gl`, etc.). For full control over the Canvas, use `<MujocoPhysics>` inside your own:
137
123
 
138
- ```tsx
139
- function MyIKController() {
140
- useBeforePhysicsStep((model, data) => {
141
- const joints = myCustomIKSolve(model, data);
142
- if (joints) {
143
- for (let i = 0; i < joints.length; i++) data.ctrl[i] = joints[i];
144
- }
145
- });
146
- return null;
147
- }
124
+ ```
125
+ <MujocoProvider> <MujocoProvider>
126
+ <MujocoCanvas config={...}> <Canvas shadows gl={...}>
127
+ <IkController config={..}> <MujocoPhysics config={...}>
128
+ <IkGizmo /> <MyController />
129
+ </IkController> </MujocoPhysics>
130
+ <MyController /> <EffectComposer>...</EffectComposer>
131
+ </MujocoCanvas> </Canvas>
132
+ </MujocoProvider> </MujocoProvider>
148
133
  ```
149
134
 
150
- ### `createController<TConfig>()` Factory
135
+ ### Custom IK Solvers
151
136
 
152
- For reusable controller plugins with typed config and default merging:
137
+ The built-in `<IkController>` uses Damped Least-Squares. Pass `ikSolveFn` to swap in your own solver (analytical, learned, etc.):
153
138
 
154
139
  ```tsx
155
- import { createController, useBeforePhysicsStep } from 'mujoco-react';
156
-
157
- interface MyConfig {
158
- gain: number;
159
- targetJoint: string;
160
- }
161
-
162
- function MyControllerImpl({ config }: { config: MyConfig; children?: React.ReactNode }) {
163
- useBeforePhysicsStep((_model, data) => {
164
- data.ctrl[0] = config.gain * Math.sin(data.time);
165
- });
166
- return null;
167
- }
140
+ import type { IKSolveFn } from 'mujoco-react';
168
141
 
169
- export const MyController = createController<MyConfig>(
170
- { name: 'MyController', defaultConfig: { gain: 1.0 } },
171
- MyControllerImpl,
172
- );
142
+ const myIK: IKSolveFn = (pos, quat, currentQ) => {
143
+ return myAnalyticalSolver(pos, currentQ); // return joint angles or null
144
+ };
173
145
 
174
- // Usage: <MyController config={{ gain: 2.0, targetJoint: 'shoulder' }} />
146
+ <IkController config={{ siteName: 'tcp', numJoints: 7, ikSolveFn: myIK }}>
147
+ <IkGizmo />
148
+ </IkController>
175
149
  ```
176
150
 
177
- ### Built-in `<IkController>`
151
+ ### `<IkController>`
178
152
 
179
- The library ships one controller out of the box — an IK gizmo you can drop in for interactive end-effector control:
153
+ The library includes one controller for interactive end-effector control:
180
154
 
181
155
  ```tsx
182
156
  <IkController config={{ siteName: 'tcp', numJoints: 7 }}>
@@ -209,7 +183,7 @@ const ikCtx = useIk({ optional: true });
209
183
  Models are loaded from any HTTP source via `SceneConfig.baseUrl`. Defaults to [MuJoCo Menagerie](https://github.com/google-deepmind/mujoco_menagerie) on GitHub.
210
184
 
211
185
  ```tsx
212
- // Menagerie robots just set robotId
186
+ // Menagerie robots: just set robotId
213
187
  const franka: SceneConfig = {
214
188
  robotId: 'franka_emika_panda',
215
189
  sceneFile: 'scene.xml',
@@ -308,7 +282,6 @@ Physics provider for use inside your own R3F `<Canvas>`. Same physics props as `
308
282
  <MujocoProvider>
309
283
  <Canvas shadows camera={{ position: [2, 2, 2] }}>
310
284
  <MujocoPhysics ref={apiRef} config={config} paused={paused}>
311
- <SceneRenderer />
312
285
  <MyController />
313
286
  </MujocoPhysics>
314
287
  <OrbitControls />
@@ -329,10 +302,6 @@ Physics provider for use inside your own R3F `<Canvas>`. Same physics props as `
329
302
  | `paused` | `boolean` | Declarative pause |
330
303
  | `speed` | `number` | Simulation speed multiplier |
331
304
 
332
- ### `<SceneRenderer />`
333
-
334
- Syncs MuJoCo bodies to Three.js meshes every frame. Must be inside `<MujocoCanvas>` or `<MujocoPhysics>`.
335
-
336
305
  ### `<IkGizmo />`
337
306
 
338
307
  drei PivotControls gizmo that tracks a MuJoCo site and drives IK on drag. Must be inside `<IkController>`.
@@ -349,10 +318,9 @@ Click-drag to apply spring forces to bodies. Raycasts to find bodies, applies `F
349
318
 
350
319
  ### R3F Group Props
351
320
 
352
- All visual components (`SceneRenderer`, `DragInteraction`, `ContactMarkers`, `Debug`, `TendonRenderer`, `FlexRenderer`) accept standard R3F group props `position`, `rotation`, `scale`, `visible`, etc.
321
+ All visual components (`DragInteraction`, `ContactMarkers`, `Debug`, `TendonRenderer`, `FlexRenderer`) accept standard R3F group props like `position`, `rotation`, `scale`, `visible`.
353
322
 
354
323
  ```tsx
355
- <SceneRenderer position={[0, 0, 1]} />
356
324
  <ContactMarkers visible={showContacts} />
357
325
  <Debug showJoints scale={0.5} />
358
326
  ```
@@ -420,9 +388,23 @@ Plays back recorded qpos trajectories with scrubbing.
420
388
 
421
389
  ## Hooks
422
390
 
391
+ ### `useMujoco()`
392
+
393
+ Access the WASM module lifecycle from any child of `<MujocoProvider>`:
394
+
395
+ ```tsx
396
+ const { mujoco, status, error } = useMujoco();
397
+ ```
398
+
399
+ | Field | Type | Description |
400
+ |-------|------|-------------|
401
+ | `mujoco` | `MujocoModule \| null` | The raw WASM module, or `null` while loading |
402
+ | `status` | `'pending' \| 'error'` | Lifecycle state (absent once loaded) |
403
+ | `error` | `string \| null` | Error message if loading failed |
404
+
423
405
  ### `useMujocoSim()`
424
406
 
425
- Access the simulation API and internal refs:
407
+ Access the simulation API and internal refs (must be inside `<MujocoCanvas>` or `<MujocoPhysics>`):
426
408
 
427
409
  ```tsx
428
410
  const { api, mjModelRef, mjDataRef } = useMujocoSim();
@@ -679,7 +661,7 @@ The full API object available via `ref` or `useMujocoSim().api`:
679
661
 
680
662
  ### Building Controllers
681
663
 
682
- See [Building Controllers](https://mujoco-react.mintlify.app/guides/building-controllers) for full patterns including config-driven controllers, IK gizmo coexistence, multi-arm support, and the `createController` factory.
664
+ See [Building Controllers](https://dadd.mintlify.app/guides/building-controllers) for full patterns including config-driven controllers, IK gizmo coexistence, multi-arm support, and the `createController` factory.
683
665
 
684
666
  ### Graspable Objects
685
667
 
@@ -701,7 +683,7 @@ sceneObjects: [{
701
683
  }]
702
684
  ```
703
685
 
704
- Without `condim: 4` and high friction, objects slide out of the gripper when lifted. See [Graspable Objects](https://mujoco-react.mintlify.app/guides/graspable-objects) for details.
686
+ Without `condim: 4` and high friction, objects slide out of the gripper when lifted. See [Graspable Objects](https://dadd.mintlify.app/guides/graspable-objects) for details.
705
687
 
706
688
  ### Click-to-Select
707
689
 
@@ -714,14 +696,14 @@ function ClickSelectOverlay() {
714
696
  }
715
697
  ```
716
698
 
717
- See [Click-to-Select](https://mujoco-react.mintlify.app/guides/click-to-select) for the full implementation.
699
+ See [Click-to-Select](https://dadd.mintlify.app/guides/click-to-select) for the full implementation.
718
700
 
719
701
  ## useFrame Priority
720
702
 
721
703
  | Priority | Owner | Purpose |
722
704
  |----------|-------|---------|
723
705
  | -1 | MujocoSimProvider | beforeStep, mj_step, afterStep |
724
- | 0 (default) | SceneRenderer, IkController, your code | Body mesh sync, IK, rendering |
706
+ | 0 (default) | SceneRenderer (internal), IkController, your code | Body mesh sync, IK, rendering |
725
707
 
726
708
  ## Roadmap
727
709
 
package/dist/index.d.ts CHANGED
@@ -718,12 +718,6 @@ declare function useIk(options: {
718
718
  optional: true;
719
719
  }): IkContextValue | null;
720
720
 
721
- /**
722
- * SceneRenderer — creates and syncs MuJoCo body meshes every frame.
723
- * Accepts standard R3F group props (position, rotation, scale, visible, etc.).
724
- */
725
- declare function SceneRenderer(props: Omit<ThreeElements['group'], 'ref'>): react_jsx_runtime.JSX.Element;
726
-
727
721
  /**
728
722
  * IkGizmo — drei PivotControls that tracks a MuJoCo site.
729
723
  *
@@ -1160,4 +1154,4 @@ interface CameraAnimationAPI {
1160
1154
  */
1161
1155
  declare function useCameraAnimation(): CameraAnimationAPI;
1162
1156
 
1163
- export { type ActuatorInfo, type BodyInfo, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControllerComponent, type ControllerOptions, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type IKSolveFn, type IkConfig, type IkContextValue, IkController, IkGizmo, type IkGizmoProps, type JointInfo, type JointStateResult, type KeyBinding, type KeyboardTeleopConfig, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoSimAPI, MujocoSimProvider, type PhysicsConfig, type PhysicsStepCallback, type PolicyConfig, type RayHit, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, SceneRenderer, SelectionHighlight, type SelectionHighlightProps, type SensorInfo, type SensorResult, type SiteInfo, type SitePositionResult, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, createController, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getContact, getName, loadScene, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIk, useJointState, useKeyboardTeleop, useMujoco, useMujocoSim, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
1157
+ export { type ActuatorInfo, type BodyInfo, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControllerComponent, type ControllerOptions, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type IKSolveFn, type IkConfig, type IkContextValue, IkController, IkGizmo, type IkGizmoProps, type JointInfo, type JointStateResult, type KeyBinding, type KeyboardTeleopConfig, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoSimAPI, MujocoSimProvider, type PhysicsConfig, type PhysicsStepCallback, type PolicyConfig, type RayHit, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, SelectionHighlight, type SelectionHighlightProps, type SensorInfo, type SensorResult, type SiteInfo, type SitePositionResult, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, createController, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getContact, getName, loadScene, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIk, useJointState, useKeyboardTeleop, useMujoco, useMujocoSim, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };