> ## Documentation Index
> Fetch the complete documentation index at: https://dadd.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# useIkController

> Set up inverse kinematics for a MuJoCo site

Hook that sets up IK control for a MuJoCo site. Pass `null` to disable IK (safe to call unconditionally per React hook rules).

## Usage

```tsx theme={null}
import { IkGizmo, ModelActuators, ModelJoints, ModelSites, useIkController } from "mujoco-react";

function MyScene({ hasIk }: { hasIk: boolean }) {
  const ik = useIkController(hasIk ? {
    siteName: ModelSites.franka.tcp,
    joints: [
      ModelJoints.franka.joint1,
      ModelJoints.franka.joint2,
      ModelJoints.franka.joint3,
      ModelJoints.franka.joint4,
      ModelJoints.franka.joint5,
      ModelJoints.franka.joint6,
      ModelJoints.franka.joint7,
    ],
    actuators: [
      ModelActuators.franka.actuator1,
      ModelActuators.franka.actuator2,
      ModelActuators.franka.actuator3,
      ModelActuators.franka.actuator4,
      ModelActuators.franka.actuator5,
      ModelActuators.franka.actuator6,
      ModelActuators.franka.actuator7,
    ],
  } : null);

  return ik ? <IkGizmo controller={ik} /> : null;
}
```

### With Custom Solver

```tsx theme={null}
import { ModelSites } from "mujoco-react";
import type { IKSolveFn } from "mujoco-react";

const myIK: IKSolveFn = ({ position, currentQ }) => {
  return myAnalyticalSolver(position, currentQ);
};

const ik = useIkController({ siteName: ModelSites.franka.tcp, ikSolveFn: myIK });
```

`useIkController` is intentionally open-ended. You can pass `ikSolveFn` for your own IK implementation, wrap the returned `IkContextValue` in your own hook, or build a plugin-style React component that reads/writes the IK target, simulation data, or actuator controls directly. Built-in helpers such as `IkGizmo` and `useKeyboardIkTarget` are optional consumers of the same controller state, not required control paths.

## Config

Pass an `IkConfig` object or `null`:

| Field           | Type                                                    | Default      | Description                                      |
| --------------- | ------------------------------------------------------- | ------------ | ------------------------------------------------ |
| `siteName`      | `string`                                                | **required** | MuJoCo site to track                             |
| `joints`        | `string \| string[] \| RegExp \| (joint) => boolean`    | inferred     | Explicit hinge/slide joints for IK               |
| `actuators`     | `string \| string[] \| RegExp \| (actuator) => boolean` | inferred     | Explicit actuators for IK output                 |
| `numJoints`     | `number`                                                | legacy only  | Contiguous qpos/ctrl count from older examples   |
| `ikSolveFn`     | `IKSolveFn`                                             | built-in DLS | Custom solver function                           |
| `damping`       | `number`                                                | `0.01`       | DLS damping                                      |
| `posWeight`     | `number`                                                | `1.0`        | Position error weight for the built-in solver    |
| `rotWeight`     | `number`                                                | `0.3`        | Orientation error weight for the built-in solver |
| `tolerance`     | `number`                                                | `1e-3`       | Solver convergence tolerance                     |
| `epsilon`       | `number`                                                | `1e-6`       | Finite-difference step for Jacobian estimation   |
| `maxIterations` | `number`                                                | `50`         | Max solver iterations                            |

By default, `useIkController` infers scalar hinge/slide joints by walking from the site body toward the model root. For nonstandard actuator layouts, pass explicit ordered names or a selector:

```tsx theme={null}
import { ModelActuators, ModelSites } from "mujoco-react";

const ik = useIkController({
  siteName: ModelSites.franka.tcp,
  actuators: [
    ModelActuators.franka.actuator1,
    ModelActuators.franka.actuator2,
    ModelActuators.franka.actuator3,
    ModelActuators.franka.actuator4,
    ModelActuators.franka.actuator5,
    ModelActuators.franka.actuator6,
    ModelActuators.franka.actuator7,
  ],
});
```

## Return Value

Returns `IkContextValue | null`. Returns `null` when config is `null`.

```ts theme={null}
interface IkContextValue {
  ikEnabledRef: React.RefObject<boolean>;
  ikCalculatingRef: React.RefObject<boolean>;
  ikTargetRef: React.RefObject<THREE.Group>;
  siteIdRef: React.RefObject<number>;
  setIkEnabled(enabled: boolean): void;
  moveTarget(pos: IkTargetPosition, duration?: number): void;
  syncTargetToSite(): void;
  solveIK(input: {
    position: IkTargetPosition;
    quaternion: IkTargetQuaternion;
    currentQ: number[];
    context?: IKSolveContext;
  }): number[] | null;
  getGizmoStats(): { pos: THREE.Vector3; rot: THREE.Euler } | null;
}

// Positions/quaternions accept a THREE object, a tuple, or a plain object:
type IkTargetPosition =
  | THREE.Vector3
  | readonly [number, number, number]
  | { x: number; y: number; z: number };
type IkTargetQuaternion =
  | THREE.Quaternion
  | readonly [number, number, number, number]
  | { x: number; y: number; z: number; w: number };
```

### Methods

| Method                                        | Description                                                                                                         |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `setIkEnabled(enabled)`                       | Enable/disable IK solving                                                                                           |
| `moveTarget(pos, duration?)`                  | Move IK target with optional animation. `pos` accepts a `THREE.Vector3`, `[x, y, z]` tuple, or `{ x, y, z }` object |
| `syncTargetToSite()`                          | Snap IK target to current site position                                                                             |
| `solveIK({ position, quaternion, currentQ })` | Solve IK manually, returns joint angles or null                                                                     |
| `getGizmoStats()`                             | Get current gizmo position and rotation                                                                             |

### Refs

| Ref                | Description                            |
| ------------------ | -------------------------------------- |
| `ikEnabledRef`     | Whether IK is currently enabled        |
| `ikCalculatingRef` | Whether IK is actively computing       |
| `ikTargetRef`      | THREE.Group representing the IK target |
| `siteIdRef`        | Resolved MuJoCo site ID                |

## Example: Keyboard/Gizmo Coexistence

A common pattern is disabling IK when keyboard control takes over. Pass the `ik` value to your controller:

```tsx theme={null}
import { IkGizmo, ModelSites, useBeforePhysicsStep, useIkController } from "mujoco-react";
import type { IkContextValue } from "mujoco-react";

function MyScene() {
  const ik = useIkController({ siteName: ModelSites.franka.tcp });

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

function MyArmController({ ik }: { ik: IkContextValue | null }) {
  useBeforePhysicsStep(({ data }) => {
    const anyKeyPressed = checkKeys();

    if (anyKeyPressed && ik?.ikEnabledRef.current) {
      syncFromCurrentCtrl(data);
      ik.setIkEnabled(false);
    }

    if (!ik?.ikEnabledRef.current) {
      writeKeyboardControl(data);
    }
  });

  return null;
}
```

The gizmo re-enables IK automatically when dragged.

## Example: Waypoint Following

```tsx theme={null}
import { ModelSites, useAfterPhysicsStep, useIkController } from "mujoco-react";

function WaypointFollower({ waypoints }: { waypoints: THREE.Vector3[] }) {
  const ik = useIkController({ siteName: ModelSites.franka.tcp });
  const [index, setIndex] = useState(0);

  useEffect(() => {
    if (!ik) return;
    ik.setIkEnabled(true);
    ik.moveTarget(waypoints[0]);
  }, [ik]);

  useAfterPhysicsStep(() => {
    if (!ik) return;
    const stats = ik.getGizmoStats();
    if (!stats) return;
    const dist = stats.pos.distanceTo(waypoints[index]);
    if (dist < 0.01 && index < waypoints.length - 1) {
      const next = index + 1;
      setIndex(next);
      ik.moveTarget(waypoints[next], 300);
    }
  });

  return null;
}
```

## Built-in Solver Details

The default solver uses **Damped Least-Squares** (DLS) with finite-difference Jacobian:

* **Position weight**: 1.0
* **Rotation weight**: 0.3
* **Tolerance**: 1e-3
* **Finite-difference epsilon**: 1e-6
* **Method**: Finite-difference Jacobian + pseudoinverse

The solver tracks the best solution across iterations and returns it even if tolerance isn't reached.

## Reset Behavior

When the simulation is reset (via `api.reset()`, `api.applyKeyframe()`, or `api.loadScene()`), useIkController automatically:

* Syncs the gizmo to the current site position
* Stops any in-progress gizmo animation
* Disables IK solving

<Warning>
  When IK is enabled, it overwrites `data.ctrl` for the arm joints inside `useBeforePhysicsStep`. Disable IK when running your own control (policies, teleoperation, etc.).
</Warning>
