# Spring hopper

A single-legged rig that bounces on a real spring. Flight is a parabola and stance is a mass on a spring, and how long each lasts is a consequence of the drop height and the spring rate rather than a duty knob.

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

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

## Notes

- The spring is drawn as a spring: a sampled helix whose coil count and radius never change, because a real one compresses by twisting its wire. It cannot pass its own solid height, and when it lands there the machine stops sinking — soften the spring under a full-height drop in the demo and you can watch it bottom out.
- Stance and flight are solved, not tweened. Contact time is the closed form for a mass on a spring under gravity, so a stiffer spring gives a shorter, harder contact at the same hop height and the duty factor moves with it.
- Illustrated, not solved: the leg swing and the reaction wheel that answers it. A free machine really does turn a wheel against its limb to aim the next landing, but the ratio here is drawn rather than an inertia model. The leg never swings while the foot is planted.
- No damping inside the stance, no travel across the frame, no motor and no energy budget — the steady hop is the ideal lossless case. An original archetype: a single-legged hopping test rig, with no manufacturer or character reproduced.

## Usage

```tsx
import { SpringHopper } from "@/components/ui/spring-hopper"

<SpringHopper behavior="hop" />

// Softer spring, taller hop: longer contact, deeper squat, same ballistics.
<SpringHopper behavior="bound" stiffness={14} height={0.9} />

// Or load the spring yourself. Controlled compression plants it on the ground.
<SpringHopper compression={0.8} interactive onCompressionChange={setLoad} />
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `view` | `"plan" | "front" | "profile" | "iso"` | `"profile"` | Where the camera stands. One rig, four projections: straight down, straight on, side elevation, or three-quarter from above. |
| `behavior` | `"hop" | "bound" | "pump" | "static"` | `"hop"` | What it does when compression is not supplied: the steady bounce, a taller one with the leg swung for the landing, or working the spring on the spot without ever leaving the ground. |
| `compression` | `number` | — | Controlled spring load, 0 free to 1 at the deepest it goes. Supplying it stops the loop and plants the machine — you cannot drive it into the air. |
| `onCompressionChange` | `(compression: number) => void` | — | Fires throughout a drag and on every arrow key, so interaction works in controlled mode too. |
| `height` | `number` | `0.5` | Apex of the hop in hop units, 0–1. One hop unit is 70 drawing units. |
| `stiffness` | `number` | `40` | Spring rate in weights per hop unit, 4–400. Static sag is its reciprocal, and it sets the contact time. |
| `speed` | `number` | `0.8` | Hops per second. |
| `interactive` | `boolean` | `false` | Drag down the frame to load the spring; release and it eases back into the behaviour. Arrows 5%, shift 15%, Home free and End fully loaded. |
| `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"` | Mast lamp: neutral, accent, or shell. |
| `showGround` | `boolean` | `true` | The ground line and the shadow, which shrinks as it rises. |
| `label` | `string` | — | Caption underneath; the blueprint variant adds the duty factor 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/spring-hopper.tsx`
