KQ-1419 · PHOTO/SCATTER
Photo Scatter
An image trail of real-looking prints: as the pointer travels, the distance it covers is measured along its path and every few dozen px a print drops at that exact point, born lifted and landing with its scale on springs.recoil, its seeded tilt on springs.snap and its shadow drawing in tight on springs.glide. After its life each print fades and shrinks away on the exit ease, oldest first, and a press pins one to the table with a strip of tape until the next press. Keyboard focus travels too, dropping prints along the way to a focused control and landing them beside it, and Enter or Space pins one there.
Coldbrook Film Lab · roll 14
Thirty-six frames of a slow summer.
on the table · move across it or press Tab
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/photo-scatter
Props
- children
- React.ReactNode
- default —
- Content on the table, such as a headline; it keeps every click, focus and key.
- images
- readonly (string | React.ReactNode)[]
- default tinted plates from the theme
- The pictures, used in turn: a string is an image URL (rendered with empty alt), anything else is rendered as the picture.
- spacing
- number
- default 90
- Distance the pointer travels between prints, in px.
- life
- number
- default 1200
- How long a print rests before it fades, in ms.
- tilt
- number
- default 8
- The most a print turns off square, in degrees; each one is seeded within ±tilt.
- size
- number
- default 112
- A print's width in px, capped so it never stands taller than 62% of the box or wider than 36% of it.
- aspect
- number
- default 0.82
- The picture's width / height.
- frame
- "instant" | "border" | "bleed"
- default "instant"
- The paper round the picture: an instant print's deep bottom margin, an even border, or none.
- paper
- string
- default a warm white
- The paper's colour, any CSS colour.
- fade
- number
- default 420
- How long a print takes to fade and shrink away, in ms.
- drop
- number
- default 0.5
- How far a print falls before it lands, 0 (placed) to 1 (tossed from high).
- max
- number
- default 14
- The most prints on the table at once; past it the oldest is hurried out.
- order
- "sequence" | "shuffle"
- default "sequence"
- Use the images in order, or in a seeded shuffle that never repeats the last one.
- rest
- number
- default 3
- Prints already lying on the table before anyone points; they leave in order after the first touch.
- origin
- readonly [number, number]
- default [0.74, 0.5]
- Where the resting prints lie, as [x, y] fractions of the box.
- pinOnPress
- boolean
- default true
- A press on the open table pins a print where it lands, taped down until the next press; presses on controls are left to the controls.
- followFocus
- boolean
- default true
- Keyboard focus travels too: prints drop along the way to a focused control and land beside it.
- interactive
- boolean
- default true
- Drop prints as the pointer travels.
- layer
- "under" | "over"
- default "under"
- Draw the prints under the content, like a table, or over it.
- paused
- boolean
- default false
- Stop dropping; prints already down finish their lives.
- seed
- number
- default 11
- Seed for the tilts, the scatter and the shuffle.
- onDrop
- (info: { total: number; image: number; pinned: boolean }) => void
- default —
- Fires on every print dropped, with the running total, which image it shows, and whether it was pinned.
- className
- string
- default —
- Classes for the table. Give it a height.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | — | Content on the table, such as a headline; it keeps every click, focus and key. |
| images | readonly (string | React.ReactNode)[] | tinted plates from the theme | The pictures, used in turn: a string is an image URL (rendered with empty alt), anything else is rendered as the picture. |
| spacing | number | 90 | Distance the pointer travels between prints, in px. |
| life | number | 1200 | How long a print rests before it fades, in ms. |
| tilt | number | 8 | The most a print turns off square, in degrees; each one is seeded within ±tilt. |
| size | number | 112 | A print's width in px, capped so it never stands taller than 62% of the box or wider than 36% of it. |
| aspect | number | 0.82 | The picture's width / height. |
| frame | "instant" | "border" | "bleed" | "instant" | The paper round the picture: an instant print's deep bottom margin, an even border, or none. |
| paper | string | a warm white | The paper's colour, any CSS colour. |
| fade | number | 420 | How long a print takes to fade and shrink away, in ms. |
| drop | number | 0.5 | How far a print falls before it lands, 0 (placed) to 1 (tossed from high). |
| max | number | 14 | The most prints on the table at once; past it the oldest is hurried out. |
| order | "sequence" | "shuffle" | "sequence" | Use the images in order, or in a seeded shuffle that never repeats the last one. |
| rest | number | 3 | Prints already lying on the table before anyone points; they leave in order after the first touch. |
| origin | readonly [number, number] | [0.74, 0.5] | Where the resting prints lie, as [x, y] fractions of the box. |
| pinOnPress | boolean | true | A press on the open table pins a print where it lands, taped down until the next press; presses on controls are left to the controls. |
| followFocus | boolean | true | Keyboard focus travels too: prints drop along the way to a focused control and land beside it. |
| interactive | boolean | true | Drop prints as the pointer travels. |
| layer | "under" | "over" | "under" | Draw the prints under the content, like a table, or over it. |
| paused | boolean | false | Stop dropping; prints already down finish their lives. |
| seed | number | 11 | Seed for the tilts, the scatter and the shuffle. |
| onDrop | (info: { total: number; image: number; pinned: boolean }) => void | — | Fires on every print dropped, with the running total, which image it shows, and whether it was pinned. |
| className | string | — | Classes for the table. Give it a height. |