Componentregistry:component
Dithering Hover
Any image turns into a black-and-white Bayer dither under the cursor. The dithered zone follows the pointer with a ragged rim and radial streaks that break up and fade, rendered live on a canvas. Controls for zone size, dot size, matrix, inks, roughness and an inverted mode. Exports drawDither for static renders.
Installation
$ npx shadcn@latest add @creative-ui/dithering-hoverOn an older shadcn CLI, use the URL: npx shadcn@latest add https://ui.krutikraut.com/r/dithering-hover.json
Preview
move the pointer over the image
Image
Mode
Zone size200px
Dot size4px
Matrix
Streaks28
Roughness0.60
Contrast1.1
Ink
Paper
Usage
Example.tsx
import { DitheringHover } from "@/components/dithering-hover"
export function Hero() {
return (
<DitheringHover
src="https://images.unsplash.com/photo-1566054757965-8c4085344c96?w=900&q=80&auto=format&fit=crop"
alt="Marble statue"
zoneSize={260}
dotSize={4}
className="aspect-[4/5] w-80 rounded-xl"
/>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| src* | string | — | Image URL. Cross-origin images must be served with CORS headers, or the canvas cannot read them. |
| alt | string | "" | Accessible description. Leave empty for decorative images. |
| zoneSize | number | 240 | Diameter of the dithered zone in CSS pixels, 60 to 500. |
| dotSize | number | 4 | Size of one dither pixel in CSS pixels, 1 to 16. 2 to 4 reads editorial, 8 to 16 reads graphic. |
| matrix | 2 | 4 | 8 | 8 | Bayer matrix order. 8 gives the smoothest tones, 2 the harshest. |
| dark | string | "#000" | Ink for dark pixels. Any CSS colour, including currentColor. |
| light | string | "#fff" | Ink for light pixels. Pass "transparent" to let the photo show through the light pixels. |
| contrast | number | 1 | Tone curve steepness, roughly 0.5 to 2. |
| brightness | number | 0 | Shift the tones lighter (positive) or darker (negative), -1 to 1. |
| streaks | number | 28 | Streaks radiating from the zone. 0 gives a clean ragged circle. |
| streakLength | number | 1 | Streak reach as a multiple of the zone radius. |
| roughness | number | 0.6 | How ragged the rim and streaks are, 0 to 1. |
| mode | "inside" | "outside" | "inside" | inside dithers under the cursor. outside dithers the whole image and reveals the photo under the cursor. |
| ease | number | 0.2 | Pointer follow easing per frame, 0 to 1. 1 snaps, smaller values lag behind. |
| seed | number | 7 | Seed for the streak layout. Change it for a different pattern. |
| fit | "cover" | "contain" | "cover" | How the source fills the canvas. |
| crossOrigin | "anonymous" | "use-credentials" | "anonymous" | Pass undefined for same-origin images that must not send a CORS request. |
| onRendered | () => void | — | Called once the image is loaded and first drawn. |
| children | React.ReactNode | — | Content layered over the canvas, such as captions or badges. |
Also accepts every div prop, which is spread onto the root element.
Also exported
- drawDither(canvas, image, width, height, options?)
- The dither on its own, with no cursor and no React. Draws an image, canvas, video or ImageBitmap onto a canvas as a full-frame Bayer dither, so you can export a PNG with
canvas.toBlob(). TakesdotSize,matrix,dark,light,contrast,brightnessandfit. - ditherBitmap(image, width, height, options?)
- Returns the raw 0/1 bitmap at one entry per dither pixel, plus its size, for your own renderers.
Notes
- Size it like an image: give it a width with
className. Without a height or aspect ratio it adopts the source image's aspect ratio once loaded. - The dither is computed once per size and option change and cached. Pointer moves only recomposite the patch around the cursor, so it stays smooth at large sizes.
- The zone grows in when the pointer enters and shrinks out when it leaves. Works with touch and pen through pointer events; with reduced motion the zone snaps instead of easing.
- Works best on high-contrast photography. Raise
contrastfor flat images. - The canvas gets
role="img"and youraltas its label, or is hidden from assistive tech whenaltis empty.
Dependencies
shadcn/utils
