# Arm 3D

The same arm as a procedural react-three-fiber rig — no model to load, and the same target puts the tip in the same place as the SVG one.

> For the complete index, see [llms.txt](https://robocn.dev/llms.txt). A Markdown version of any page is available by appending `.md` to its URL or by sending an `Accept: text/markdown` header.

## Install

```bash
bunx --bun shadcn@latest add https://robocn.dev/r/robot-arm-3d.json
```

Registry item: `robot-arm-3d` · [`https://robocn.dev/r/robot-arm-3d.json`](https://robocn.dev/r/robot-arm-3d.json)

## Notes

- The drawing is a section through the arm’s own vertical plane, projected: side elevation is the drawing it always had, and the castings, the pedestal and the base plate gain their width across the machine as the camera comes round.
- Colours resolve through `robot-color`, so CSS variables and oklch() reach three.js, and a dark-mode toggle retints the rig without a remount.

## Usage

```tsx
import { RobotArm3D } from "@/components/ui/robot-arm-3d"
import { RobotStage } from "@/components/ui/robot-stage"

export function Cell() {
  return (
    <RobotStage floor="grid" className="h-96">
      <RobotArm3D behavior="orbit" tool="welder" links={[1, 0.82, 0.34]} />
    </RobotStage>
  )
}
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `links` | `number[]` | `[1, 0.82, 0.34]` | Relative link lengths, scaled to fill reach. |
| `reach` | `number` | `2.4` | Total stretch in three.js world units. |
| `target` | `Vec3 | ((clock: number) => Vec3) | null` | `null` | Controlled tip position, or a scripted path. |
| `behavior` | `"pointer" | "orbit" | "sweep" | "idle" | "static"` | `"idle"` | Pointer maps the cursor onto a plane in front of the arm. |
| `tool` | `RobotTool` | `"gripper"` | End effector. |
| `wireframe` | `boolean` | `false` | Render the rig as wireframe. |
| `position / rotation / scale` | `[number, number, number] | number` | — | Placed like any other three.js object. |
| `onPose` | `(joints: Vec3[]) => void` | — | Called every frame outside React state, for readouts. |
| `interactive` | `boolean` | `false` | Press the floor and the tip goes there, following the pointer until it is released. Picking against a plane is what separates driving the arm from orbiting the camera around it. |
| `onTargetChange` | `(target: Vec3 | null) => void` | — | The picked floor position in world units, and null on release. |
| `speed` | `number` | — | Tip travel in world units per second. |
| `animate` | `boolean` | `true` | Off snaps to a fixed goal or samples a scripted path at phase, then stops. Reduced-motion preference does the same. |
| `paused` | `boolean` | `false` | Freeze in place. |
| `phase` | `number` | `0` | Seconds of offset, so a row of machines breaks step. |
| `color` | `string` | `var(--robot-shell)` | Body panels — the colour the machine reads as. |
| `accent` | `string` | `var(--robot-accent)` | Status colour: tip light, live tool, readouts. |
| `metal` | `string` | `var(--robot-metal)` | Bare machined parts: collars, bolts, tool bodies. |
| `dark` | `string` | `var(--robot-dark)` | Cast joints, base, shadow side. |
| `palette` | `Partial<RobotPalette>` | — | Override any subset of roles at once, including glow and grid. |

## Source

- `src/components/ui/robot-arm-3d.tsx`
