# Ball hopper

A bounding sensor ball whose shell is its own compliance. It flattens on impact at constant volume — so it has to get exactly that much wider — and with a restitution below one it bounces lower each time and comes to rest.

> 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-hopper.json
```

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

## Notes

- The squash conserves volume: an oblate spheroid with rx² ry = r³, so flattening the shell widens it by exactly that much. Because the orthographic projection of a spheroid is exactly an axis-aligned ellipse, a squashed ball reads as a flatter one in the elevations and a wider one in plan view, out of one geometry and with no artwork per angle.
- It comes to rest in finite time, which is not free: contact time does not go to zero as the landing speed does, so an ideal Zeno bounce never finishes. The sequence ends when the rebound can no longer lift the shell clear, and it then sits at its static sag.
- Illustrated, not solved: the contact patch — a constant-volume shell stays tangent to the ground, so the patch is drawn rather than cut — and the yaw, which does not come out of the contact. The band, lugs, vents and optic sit on the shell's own surface and are drawn only where a camera can see them.
- Not orb-droid: that one rolls, never leaves the ground, and has a rigid shell. Nothing travels across the frame, and no friction, spin-up or material is modelled. An original archetype: a throwable bounding sensor ball.

## Usage

```tsx
import { BallHopper } from "@/components/ui/ball-hopper"

<BallHopper behavior="bounce" />

// Dropped and left alone: each bounce is e² of the last, then it sits down.
<BallHopper behavior="settle" restitution={0.55} />

// Or hold it up yourself and let go.
<BallHopper altitude={1} interactive onAltitudeChange={setHeight} />
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `view` | `"plan" | "front" | "profile" | "iso"` | `"front"` | Where the camera stands. One shell, four projections: straight down, straight on, side elevation, or three-quarter from above. |
| `behavior` | `"bounce" | "settle" | "skitter" | "static"` | `"bounce"` | What it does when altitude is not supplied: the steady bounce, a whole settling sequence a cycle, or fast low bounces with the band turning. |
| `altitude` | `number` | — | Controlled height, 0 on the ground to 1 at the top of its drop. Supplying it stops the loop and holds it there — off the ground, so not touching and not squashed. |
| `onAltitudeChange` | `(altitude: number) => void` | — | Fires throughout a drag and on every arrow key. |
| `spin` | `number` | — | Which way the sensor band faces, in degrees. Omit and it turns as it bounces. |
| `height` | `number` | `0.6` | Apex of the bounce in hop units, 0–1. One hop unit is 70 drawing units. |
| `stiffness` | `number` | `40` | Shell stiffness in weights per hop unit, 4–400. Sets the contact time and how deep the squash goes. |
| `restitution` | `number` | `0.66` | Fraction of the landing speed returned at take-off, 0–1. Apexes decay by its square, which is what drives settle. |
| `speed` | `number` | `0.7` | Bounces a second, or settling sequences a second when the behavior is settle. |
| `interactive` | `boolean` | `false` | Drag up the frame to lift it; release and it drops. Arrows 5%, shift 15%, Home on the ground and End at the top. |
| `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. |
| `signal` | `"idle" | "ready" | "warning"` | `"ready"` | Optic lamp: neutral, accent, or shell. |
| `showGround` | `boolean` | `true` | The horizon line and the shadow, which shrinks as it rises. |
| `label` | `string` | — | Caption underneath; the blueprint variant adds the restitution to it. |
| `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-hopper.tsx`
