Skip to content

Components / Backgrounds

Dither

Draws slow noise waves on the GPU, ordered-dithered into pixel cells of one colour on the page's ground, for the look of a 1-bit screen. The pointer lifts the field around it. The drawing buffer is one pixel per cell, scaled up pixelated by CSS, so the shader touches a ninth of the pixels at the default cell. Every constant is a custom property re-read when the element's style changes. Without WebGL2 or JavaScript a still dot grid stands in. Fills its positioned parent.

Live demo

Dither.

move your cursor

Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.

Measured

JavaScript with its libs

10.8 kB raw

1.2 kB raw of its own, 776 B gzip. The rest is the runtime, the shader runner, the canvas helper and the GL core, loaded once for every effect on the page.

CSS 5.1 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.

Browser support

Baseline · widely available

Chrome 56 · Firefox 51 · Safari 15; needs webgl2. Elsewhere: without WebGL2, a still dot grid in the colour, fading toward the edges.

Install Pro

The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:

terminal
npx shadcn@latest add @moonarc-pro/dither
src/pages/index.astro
<section class="relative min-h-[60vh]">
  <Dither color="var(--primary)" pixel={3} />
  <h1 class="relative">A 1-bit dither background</h1>
</section>

Without a key the registry answers 401 with an upgrade message. Related free primitives: glyph-field.

Props

PropTypeDefaultDescription
colorstring'oklch(0.62 0.19 262)'Dot colour; any CSS colour, theme tokens included. Re-read when the theme changes.
pixelnumber3Cell size in CSS px. The canvas is one pixel per cell, so a larger cell is also cheaper.
scalenumber2.4Waves across the short side.
speednumber1Wave speed multiplier; a change never jumps the waves (time is accumulated).
densitynumber0.02Tone added everywhere: 0 leaves the troughs empty, 0.3 fills them with a light dither.
contrastnumber0.95How bright the wave ridges get.
levelsnumber2Tone steps per cell: 2 is 1-bit, 4 adds two partial tones.
liftnumber0.5How much the pointer lifts the field around it; 0 turns the pointer off.

Reduced motion

No loop: one frame at a fixed moment is drawn and kept; the pointer does not lift it.

With ClientRouter

Bound through the shared runtime: loop, observers and pointer listener are released before the swap and the WebGL context is lost on purpose after it; five round trips leave one context. Under transition:persist the canvas keeps its context and starts again on the next page.

Why it is built this way

Replaces: Dither (React Bits) · Dithering shaders (Paper). See the migration table.

A field of braille dots that reads as a flowing contour map: two waves cross it, the colour turns along a sunset arc, and the cursor carves a trail that decays one dot at a time.

newly · Chrome 125 · Firefox 128 · Safari 17.2pointeralways

Draws smoke that follows the cursor.

every browserpointeralways

Silk

Pro

Draws a sheet of fabric drifting in slow folds, lit from one side: a warped height field on the GPU, normals from finite differences, a diffuse body in the colour and a narrow sheen in a second tone.

every browseralwayspointer

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown