Spindle
R3F film-bobbin gallery: frames on a continuous sprocketed film strip wound helically around a reel, unwinding frame by frame into a flat contact strip as you scroll. Drag to spin or pan, arrow keys to browse, click or Enter to lift a frame into a lightbox. Off-focus duotone, idle spin, offscreen-paused, reduced-motion-safe.
r3fpro
Pro
The preview is live. The source is Pro.
One purchase, lifetime access: every pro component's source, CLI install with your own registry token, and all future drops.
Installation
CLI
npx shadcn@latest add @crucible/spindleManual — install dependencies, then copy the source
npm install three @react-three/fiberProps
| Prop | Type | Default | Description |
|---|---|---|---|
| items | SpindleItem[] | nine seeded procedural plates | Frames wound onto the bobbin. |
| progress | number | Controlled unwind, 0 (wound on the bobbin) → 1 (flat contact strip). When set, scroll and wheel input no longer drive the unwind. | |
| scrollDriven | boolean | true | Drive the unwind from scroll: the component becomes a tall pin track (pinLength × the scroller's viewport) with a sticky stage inside. When false (and uncontrolled), wheel / vertical drag over the stage unwinds instead. |
| scroller | React.RefObject<HTMLElement | null> | HTMLElement | null | the nearest scrollable ancestor, else the window | The scroll container that drives the unwind. |
| pinLength | number | 2.6 | Pin track length as a multiple of the scroller's viewport height. |
| duotone | [string, string] | false | cool silver | Off-focus duotone as [ink, paper] hex colors; the focused frame shows full color. false keeps every frame in full color. |
| interactive | boolean | true | Drag to spin / pan, click to open, wheel to unwind. Keyboard always works. |
| paused | boolean | false | Freeze the idle spin and the intro; transitions still ease on demand. |
| label | string | "Film gallery" | Accessible name for the gallery. |
| children | React.ReactNode | Overlay content laid over the stage (pointer events pass through to the film). | |
| className | string | Extra classes for the root (the pin track when scroll-driven). | |
| ref | React.Ref<SpindleHandle> | Imperative handle: unwind(), rewind(), open(i), close(). |
Honors prefers-reduced-motion with a designed static fallback, pauses offscreen and on hidden tabs, and passes className through.