KQ-1566 · SHUTTER/SPILL
Shutter Spill
A camera on a light table fires a burst of contact prints at `cadence` frames a second: each shot blinks the lens, throws a flash across the table, and sends a print out white, developing as it travels a loose spiral on springs.glide to a seeded resting place on a golden-angle spiral, where its angle lands on springs.snap. A click lifts a print into the middle on springs.glide while the others shuffle aside in a cascade, a double click gathers the set back into the lens, and a print slides 1:1 under the finger and coasts by projection. The prints are one roving tab stop: arrows step through them (carrying a held print along), Enter picks up, Escape puts down, Shift with an arrow slides one, and the camera button fires or gathers.
Fieldline darkroom · roll 12
Morning on the coast
The camera fires the frames onto the table or gathers them back. Arrow keys step through the frames, Enter picks one up and Escape puts it down; Shift with an arrow slides a frame across the table.
loaded · 8 frames, press the camera
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/shutter-spill
Props
- items
- { src?: string; alt: string; caption?: string }[]
- default eight drawn prints
- The pictures, one print each, up to 24; prints without a src are drawn in code.
- cadence
- number
- default 8
- The burst's rhythm in frames per second, 3 to 15.
- spread
- number
- default 0.85
- How far across the table the spiral reaches, 0.3 to 1.
- flash
- number
- default 0.6
- Strength of each shot's flash, 0 (none) to 1.
- swirl
- number
- default 0.3
- How far each print curls round on its way out, in turns, 0 to 1.
- tilt
- number
- default 7
- The largest angle a print comes to rest at, degrees.
- value / defaultValue
- number | null
- default null
- The picked-up print, controlled or initial.
- onValueChange
- (index: number | null) => void
- default —
- Fires from the click or key that picked a print up or put it down.
- spilled / defaultSpilled
- boolean
- default false
- Whether the prints are out on the table, controlled or initial.
- onSpilledChange
- (spilled: boolean) => void
- default —
- Fires from the trigger, camera, double click or key that fired or gathered the set.
- trigger
- "inView" | "mount" | "manual"
- default "inView"
- What fires the first burst: coming into view, mounting, or only the camera.
- light
- string
- default "oklch(from var(--warn) 0.94 0.04 h)"
- The table's light, any CSS colour; it is mixed into the card colour.
- frameSize
- number
- default fitted
- A print's width on the table, px.
- numbered
- boolean
- default true
- Print frame numbers in the margins.
- seed
- number
- default 1
- Seeds the spiral's jitter and the resting angles.
- sound
- boolean
- default false
- A shutter click for each frame of a burst a visitor fired.
- label
- string
- default "Contact prints"
- The table's accessible name.
- disabled
- boolean
- default false
- No firing, picking or sliding.
- className
- string
- default —
- Sizes the table; it fills its parent by default (h-full w-full).
| Prop | Type | Default | Description |
|---|---|---|---|
| items | { src?: string; alt: string; caption?: string }[] | eight drawn prints | The pictures, one print each, up to 24; prints without a src are drawn in code. |
| cadence | number | 8 | The burst's rhythm in frames per second, 3 to 15. |
| spread | number | 0.85 | How far across the table the spiral reaches, 0.3 to 1. |
| flash | number | 0.6 | Strength of each shot's flash, 0 (none) to 1. |
| swirl | number | 0.3 | How far each print curls round on its way out, in turns, 0 to 1. |
| tilt | number | 7 | The largest angle a print comes to rest at, degrees. |
| value / defaultValue | number | null | null | The picked-up print, controlled or initial. |
| onValueChange | (index: number | null) => void | — | Fires from the click or key that picked a print up or put it down. |
| spilled / defaultSpilled | boolean | false | Whether the prints are out on the table, controlled or initial. |
| onSpilledChange | (spilled: boolean) => void | — | Fires from the trigger, camera, double click or key that fired or gathered the set. |
| trigger | "inView" | "mount" | "manual" | "inView" | What fires the first burst: coming into view, mounting, or only the camera. |
| light | string | "oklch(from var(--warn) 0.94 0.04 h)" | The table's light, any CSS colour; it is mixed into the card colour. |
| frameSize | number | fitted | A print's width on the table, px. |
| numbered | boolean | true | Print frame numbers in the margins. |
| seed | number | 1 | Seeds the spiral's jitter and the resting angles. |
| sound | boolean | false | A shutter click for each frame of a burst a visitor fired. |
| label | string | "Contact prints" | The table's accessible name. |
| disabled | boolean | false | No firing, picking or sliding. |
| className | string | — | Sizes the table; it fills its parent by default (h-full w-full). |