Skip to main content
A controller is a React component that calls useBeforePhysicsStep to write data.ctrl each frame and renders null. The useIkController() hook follows this same pattern. You can use it, swap in your own IK solver, or write your own controller from scratch.

Pattern: Simple Keyboard Bindings

For robots where arm control comes from <IkGizmo />, the controller only adds extra bindings (gripper, etc.):
Drop it into your scene as a child of <MujocoCanvas>:

Pattern: Custom Physics-Step Control

For more complex control (IK solvers, velocity control, state machines), use useBeforePhysicsStep to write directly to data.ctrl each frame.

Keyboard State

Read keyboard input via window event listeners and a ref:

Config-Driven Arm Controller

A generic hook that accepts a static config object makes it easy to support multiple robots. Each robot is a different config:
The hook reads keyboard state and writes to the correct actuator handles each frame:
Then each robot controller is just a config:

Custom IK Solvers

Three options for IK:

1. Use the built-in solver

The default useIkController() uses Damped Least-Squares:

2. Plug in your own solver

Pass ikSolveFn to replace the built-in solver while keeping the gizmo, reset handling, and context:

3. Skip useIkController entirely

Solve IK yourself inside useBeforePhysicsStep with full access to the model and data:
This gives you full access to model/data for whatever solver you want.

Pattern: Reusable Plugins with createControllerHook

For reusable controllers with typed config and default merging, use the createControllerHook factory. It stabilizes config references (so inline objects don’t cause re-renders), merges defaults, and supports disabling via null.

createControllerHook API

Pass null to disable the controller without breaking the rules of hooks — useImpl is always called, it just receives null and should no-op.

Pattern: Reusable Plugins with createController

The createController factory is the component equivalent — same config stabilization and default merging, but returns a component that can render children:

createController API

The returned component accepts config (merged with defaults) and optional children. It also exposes static metadata: MyController.controllerName and MyController.defaultConfig.

Providing Context to Children

Controllers can provide state to descendants via React context:

Listening for Resets

Register a callback to reset your controller state when the simulation resets:
The library’s useIkController() hook demonstrates all these patterns: reset handling, useBeforePhysicsStep for solving, and useFrame for gizmo animation.

Coexisting with IK Gizmo

When a robot supports both gizmo drag and keyboard control, the controller needs to:
  1. Accept ik as a prop (the IkContextValue from useIkController())
  2. Sync state on transition: when the user switches from gizmo to keyboard, read data.ctrl to avoid a position jump
  3. Disable IK via ik.setIkEnabled(false) when taking over
Pass the ik value from useIkController() to the controller as a prop. The gizmo re-enables IK automatically when dragged.

Composing Controllers in Your Scene

Controllers are React children. Swap them based on state:

Performance Tips

  • Use for loops instead of .forEach / .map in useBeforePhysicsStep (it runs every physics tick)
  • Store keyboard state in a useRef, not useState (avoids re-renders at 60fps)
  • Cache actuator IDs once (not every frame) using findActuatorByName
  • Keep the callback closure stable; avoid creating new functions each render