KQ-1624 · FOVEA/IMAGE

Fovea Image

A picture drawn coarse in square blocks everywhere except where you look: under the pointer it sharpens in steps, a crisp centre and rings that each halve the block, chosen block by block so the ring edges step like a real change of resolution, every block the average of the picture beneath it. The sharp spot follows the pointer as an eye does, waiting a moment and then jumping on springs.snap, whose one overshoot is a saccade's small glissade; entering closes the field in onto the gaze on springs.glide and leaving opens it out on springs.drift until the picture is sharp all over. Keyboard focus shows it sharp, the arrow keys look around it and Escape stops looking.

KQ-1624 · FOVEA/IMAGE

Coldbrook Harbour Authority · tonight

Evening berths · Coldbrook harbour

at rest · sharp all over

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/fovea-image

Props

src
string
default —
The picture's URL; without it a harbour at dusk is drawn in code.
alt
string
default "Coldbrook harbour at dusk: …"
Describes the picture; it is the figure's accessible name.
blockSize
number
default 20
The coarse field's block size in px.
fovea
number
default 48
The crisp centre's radius in px.
steps
number
default 2
How many rings of resolution lie between the crisp centre and the coarse field, 1 to 4; each halves the block.
ring
number
default 0.7
Each ring's width as a share of the fovea, 0.2 to 2.
lag
number
default 70
How long the eye waits before it jumps to where the pointer went, in ms.
rest
"sharp" | "coarse"
default "sharp"
What the picture is with no one looking: sharp all over, or coarse all over.
intro
boolean
default true
On first view the eye glances round the picture, three fixations, before it settles.
gap
number
default 0
A seam between coarse blocks in px, for a tiled look.
fit
"cover" | "contain"
default "cover"
Fill the box, cropping, or fit inside it.
caption
React.ReactNode
default —
A caption laid over the foot of the picture.
onGazeChange
(gaze: { x: number; y: number } | null) => void
default —
Fires when the eye settles on a new point, as fractions of the picture, and with null when it stops looking.
disabled
boolean
default false
Shows the picture at rest and takes no input.
paused
boolean
default false
Holds everything where it is: no intro, no saccades.
seed
number
default 4
Seeds the drawn harbour.
dpr
number
default 2
Cap on the canvas's pixel ratio, at most 2.
className
string
default "h-full w-full"
Sizes the box; the picture fills it. Give it a height.