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