# Ball launcher

Two counter-rotating wheels: the ball leaves at the mean of their surface speeds and turns at their difference over its own diameter. Both numbers are on the readout.

> 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/ball-launcher.json
```

Registry item: `ball-launcher` · [`https://robocn.dev/r/ball-launcher.json`](https://robocn.dev/r/ball-launcher.json)

## Notes

- Exit speed is the mean of the two contact speeds and spin is their difference over the ball's own diameter, both out of launcherExit off the same pair of inputs. Matched wheels throw it flat and fast; every turn of mismatch trades speed for rotation.
- The wheels are drawn at the speeds they are given — the spokes index by the clock times the rate — so a wheel at half speed visibly turns at half speed.
- No slip, no compression and no air. A real launcher loses some of the contact speed to the ball skidding through the gap; this one reports the ideal, which is the number the machine is set to.

## Usage

```tsx
import { BallLauncher } from "@/components/ui/ball-launcher"

<BallLauncher behavior="feed" elevation={30} />

// Controlled: mismatch the wheels and the spin is the difference.
<BallLauncher top={48} bottom={16} />
<BallLauncher interactive onWheelsChange={setWheels} />
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `view` | `"plan" | "front" | "profile" | "iso"` | `"profile"` | Where the camera stands. One machine, four projections: straight down, straight on, side elevation, or three-quarter from above. |
| `top` | `number` | `34` | Top wheel speed in turns per second. Supplying either wheel stops the loop from picking them. |
| `bottom` | `number` | `22` | Bottom wheel speed in turns per second. Mismatch is spin. |
| `behavior` | `"feed" | "spin" | "idle" | "static"` | `"feed"` | Feed runs balls through; spin brings the wheels up with nothing going between them. |
| `elevation` | `number` | `26` | Barrel elevation in degrees. The wheels, the chute and the muzzle all lie on that axis. |
| `speed` | `number` | `0.5` | Cycles per second. |
| `animate` | `boolean` | `true` | Off parks the machine at phase and stops rendering. A reduced-motion preference does the same. |
| `paused` | `boolean` | `false` | Freeze where it stands. |
| `phase` | `number` | `0` | Seconds of offset, so a row of machines breaks step. |
| `interactive` | `boolean` | `false` | Drag up and down to bias the wheels, which is to dial the spin in. |
| `onWheelsChange` | `(wheels: { top: number; bottom: number }) => void` | — | Both wheel speeds throughout a drag or a key press. |
| `showGround` | `boolean` | `true` | Draw the contact shadow under the tripod. |
| `label` | `string` | — | Caption below the readout. |
| `variant` | `"solid" | "outline" | "blueprint" | "wire"` | `"solid"` | How the machine is painted. Geometry never changes between variants. |
| `size` | `"xs" | "sm" | "md" | "lg" | "xl" | number` | `"md"` | Rendered width in pixels, or a step on the scale. |
| `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/ball-launcher.tsx`
