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.

KQ-1419 · PHOTO/SCATTER

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.