Skip to main content
IK is provided by the useIkController() hook — it is not part of the core MujocoSimAPI. The hook returns an IkContextValue that you pass to <IkGizmo> or use directly.

Setup

import { useIkController, IkGizmo } from "mujoco-react";

function MyScene() {
  const ik = useIkController({ siteName: "tcp", numJoints: 7 });

  return (
    <>
      {ik && <IkGizmo controller={ik} />}
      <MyIkConsumer ik={ik} />
    </>
  );
}

IkContextValue Methods

setIkEnabled(enabled)

Enable or disable the IK solver.
ik?.setIkEnabled(true);   // IK writes to ctrl each frame
ik?.setIkEnabled(false);  // IK disabled — you control ctrl
When IK is enabled, it overwrites data.ctrl for the arm joints inside useBeforePhysicsStep. Disable IK when running your own control (policies, teleoperation, etc.).

moveTarget(pos, duration?)

Animate the IK target (gizmo) to a new position.
ik?.moveTarget(new THREE.Vector3(0.5, 0, 0.3));       // Instant
ik?.moveTarget(new THREE.Vector3(0.5, 0, 0.3), 500);  // 500ms animation

syncTargetToSite()

Snap the IK gizmo to the current site position.
ik?.syncTargetToSite();
Useful after programmatic joint changes to re-align the gizmo with the actual end-effector.

solveIK(pos, quat, currentQ)

Run IK solving manually (without the gizmo).
const targetPos = new THREE.Vector3(0.5, 0, 0.3);
const targetQuat = new THREE.Quaternion();
const currentJoints = Array.from(api.getQpos()).slice(0, 7);

const solution = ik?.solveIK(targetPos, targetQuat, currentJoints);
if (solution) {
  const qpos = api.getQpos();
  for (let i = 0; i < solution.length; i++) {
    qpos[i] = solution[i];
  }
  api.setQpos(qpos);
}
Returns: number[] | null — joint positions, or null if solver failed.

getGizmoStats()

Get the current IK gizmo position and orientation.
const stats = ik?.getGizmoStats();
if (stats) {
  console.log("Gizmo position:", stats.pos);
  console.log("Gizmo rotation:", stats.rot);
}
Returns: { pos: THREE.Vector3, rot: THREE.Euler } | null

Disabling IK

Pass null to useIkController() to disable IK entirely. This is safe to call unconditionally (React hook rules):
const ik = useIkController(hasIk ? ikConfig : null);
// ik is null when hasIk is false

Custom IK Solver

Pass ikSolveFn to the config to replace the built-in solver:
import type { IKSolveFn } from "mujoco-react";

const myIK: IKSolveFn = (pos, quat, currentQ) => {
  const solution = myAnalyticalSolver(pos, currentQ);
  return solution;
};

const ik = useIkController({ siteName: "tcp", numJoints: 7, ikSolveFn: myIK });
When to use a custom solver:
  • Analytical IK — faster and more reliable for specific robot geometries
  • Learned IK — neural network solvers trained on your robot
  • External solvers — calling into WASM-compiled libraries (e.g. KDL, TRAC-IK)
  • Constrained IK — solvers that enforce joint limits, collision avoidance, or task-space constraints

Built-in Solver Details

The default solver uses Damped Least-Squares (DLS) with finite-difference Jacobian:
  • Max iterations: 50 per frame (configurable)
  • Damping: 0.01 (configurable)
  • Position weight: 1.0
  • Rotation weight: 0.3
  • Tolerance: 1e-3
  • Method: Finite-difference Jacobian + pseudoinverse