# Dither (Moonarc)

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.

- Pro block. Install: `npx shadcn@latest add @moonarc-pro/dither` (license key required)
- Tier C · category background · trigger always, pointer
- Readout: `<Dither pixel={3} levels={2}>`
- Browser support: widely (Chrome 56 · Firefox 51 · Safari 15); elsewhere: without WebGL2, a still dot grid in the colour, fading toward the edges
- Measured cost: 1.2 kB raw JS · 776 B gzip · + runtime + shader + canvas + gl, 10.8 kB raw in all (with dependencies 10.8 kB raw; CSS 5.1 kB raw)
- Page: https://moonarc.dev/components/dither/

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `color` | `string` | `'oklch(0.62 0.19 262)'` | Dot colour; any CSS colour, theme tokens included. Re-read when the theme changes. |
| `pixel` | `number` | `3` | Cell size in CSS px. The canvas is one pixel per cell, so a larger cell is also cheaper. |
| `scale` | `number` | `2.4` | Waves across the short side. |
| `speed` | `number` | `1` | Wave speed multiplier; a change never jumps the waves (time is accumulated). |
| `density` | `number` | `0.02` | Tone added everywhere: 0 leaves the troughs empty, 0.3 fills them with a light dither. |
| `contrast` | `number` | `0.95` | How bright the wave ridges get. |
| `levels` | `number` | `2` | Tone steps per cell: 2 is 1-bit, 4 adds two partial tones. |
| `lift` | `number` | `0.5` | How much the pointer lifts the field around it; 0 turns the pointer off. |

## Usage

```astro
<section class="relative min-h-[60vh]">
  <Dither color="var(--primary)" pixel={3} />
  <h1 class="relative">A 1-bit dither background</h1>
</section>
```

## 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.

## Craft

- A 4 × 4 Bayer matrix, not random noise: ordered dithering keeps the gradient readable and still between frames, where random thresholds would shimmer.
- One buffer pixel per cell with image-rendering: pixelated. The cells are crisp at any DPR and the GPU does a ninth of the work at pixel 3.
- Wave ridges from a sine over domain-warped noise raised to the sixth power: thin bright crests, a dithered falloff and empty troughs, so the page's ground shows through.
- Time is accumulated × speed, so a tune panel changes the pace without the waves jumping.
- The pointer eases in over ~150 ms and out when it leaves, so it lifts the field instead of drawing a cursor sprite.

## Replaces

- Dither (React Bits)
- Dithering shaders (Paper)

## Source

Pro block. The source is served by the license-gated registry: `npx shadcn@latest add @moonarc-pro/dither` with a key in components.json (https://moonarc.dev/account/). Related free primitives: glyph-field.
