For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. The dithercn agent skill, for building any dither on the engine, is at /.well-known/agent-skills/dithercn/SKILL.md.
dithercn
GitHub

Moiré

Two fine gratings laid over each other and sliding slowly, so huge interference bands bloom, sweep and turn across the box, as when two window screens overlap.

Controls
Colour
Variant
Pitch4
Bands3
Angle30°
Drift1
Warp0.6
Animate

Installation

pnpm dlx shadcn@latest add https://dithercn.dev/r/moire.json

Usage

import { Moire } from "@/components/dithercn/moire";
 
export function Hero() {
  return (
    <section className="relative isolate min-h-[480px]">
      <Moire className="absolute inset-0 -z-10" />
      <h1>Your headline</h1>
    </section>
  );
}

Two gratings of fine lines sit on top of each other. Each line is a whole number of dither cells wide, every pitch cells, so the lines stay crisp at any size. A line of one grating is drawn in the second palette colour, and a cell where lines of both gratings cross is drawn in the last. Where the gratings line up, their lines overlap into crisp ink stripes on open paper. Where they fall half a pitch apart, they fill each other's gaps with a flat tint. Between the two, the stripes thicken and soften, and the eye reads the change as broad bands many times larger than the lines themselves.

The bands move far more than the lines do. The gratings slide apart by a fraction of a cell per second, and the bands sweep across the box at dozens of pixels per second. At the same time the angle between the gratings breathes, so the bands swell, narrow and swing. The second grating is never quite flat: warp bends it by a cell or two in a few broad swells that change shape slowly. That bend is too small to see in the lines, but it curves the bands by most of their width. There are three variants:

  • "lines": two sets of straight lines a few degrees apart. The bands run across the lines.
  • "rings": two sets of concentric rings whose centres drift apart and together. The bands fan out from the middle as a slowly turning starburst.
  • "mesh": two square grids, like a pair of window screens. The bands form a large lattice of their own.

bands sets how many bands cross the box, not how far the lines are turned, so a phone shows the same composition as a desktop.

Examples

Rings in a palette

colors gives each band the next colour from a list in turn. The lines that cross take the band's colour and the rest a tint of it, and the colours change halfway between two bands, inside the flat tint, so no colour edge cuts across the crisp stripes. With "rings", the bands fan out from the middle, so the colours do too. Ring colours run into each other across most of each band, and they fade towards grey near the middle, where every band meets, so the centre shows the rings' own interference rather than slices of colour.

Behind a headline

mask="center" clears an oval in the middle of the box, and the lines dissolve into dots towards it. Two big bands and two soft tints of the ink keep the pattern quiet behind the text.

Props

PropTypeDefault
variant

Two sets of straight lines, two sets of concentric rings, or two square grids.

Type: "lines" | "rings" | "mesh"Default "lines"
pitch

Spacing of each grating's lines, in whole dither cells, from 3 to 12 (4 to 12 for mesh). Each line is half a pitch wide.

Type: numberDefault 4
bands

Roughly how many interference bands cross the box, from 1 to 8. Fewer bands are bigger.

Type: numberDefault 3
angle

Direction of the lines in degrees, as in CSS: 0 runs up, 90 across. For rings, the direction from one ring centre to the other.

Type: numberDefault 30
center

The point the gratings turn around, or the midpoint between the ring centres, as fractions of the box from the top left.

Type: [x: number, y: number]Default [0.5, 0.5]
drift

How fast the gratings slide against each other, so the bands sweep across the box. 0 stops the slide; the bands still turn and breathe.

Type: numberDefault 1
warp

How much the second grating bends, so the bands curve and twist instead of running straight, from 0 (flat) to 2.

Type: numberDefault 0.6
mask

Fades it to paper where your text sits.

Type: "none" | "center" | "top" | "bottom" | "edges"Default "none"
colors

Colour each band from this list in turn. Palette names such as "blue", CSS variable names, or any CSS colour. The palette then only sets the paper.

Type: [DitherColor, ...DitherColor[]]Default –
palette

Colours from paper to ink. Lines of one grating take the colour halfway along, cells where both cross the last. With two colours, lines that don't cross dither to half.

Type: [DitherColor, DitherColor, ...DitherColor[]]Default Paper, a tint of --dither-ink, then --dither-ink
matrix

The threshold pattern, seen where a mask fades the lines. See Dither engine.

Type: "bayer2" | "bayer4" | "bayer8" | "blue-noise"Default "bayer8"
cell

Size of one dither cell in CSS pixels, and so of the finest line.

Type: numberDefault --dither-cell
speed

How fast everything moves.

Type: numberDefault 1
animate

false stops the motion: a still frame if it starts off, the current frame if it was moving.

Type: booleanDefault true
className

Size and position. The gratings fill this box.

Type: stringDefault –

Notes

  • It is decorative and hidden from assistive technology. Put real text on top of it, not inside it.
  • With reduced motion turned on, it draws one still frame and stops.
  • It pauses when scrolled out of view or when the tab is hidden, and releases its WebGL context when far offscreen. Without WebGL it draws the same two gratings as CSS repeating gradients, in the palette's middle colour, with the last colour where their lines cross, so the moiré still appears. An SVG displacement filter bends the second grating by warp, so the bands curve, though not in exactly the same shape as the shader's. With colors, the colour stripes stay straight while the bands bend, and the ring colours blend smoothly instead of as dots.
  • Each line is drawn in whole cells and every cell is either on a line or not, so the fine pattern never blurs into grey and never flickers. No line moves more than a cell every few seconds; only the bands move quickly.
  • It costs a few dot products per cell, at 30 fps and one pixel per dither cell. See Performance.