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

Dither image

Any photo dithered to a preset or your own palette, with crisp pixels and a coarse-to-fine reveal as it loads.

Controls
Preset
Ink (ink, duotone)
Method
Sharpen0.3
Invert

Installation

pnpm dlx shadcn@latest add https://dithercn.dev/r/dither-image.json

Usage

import { DitherImage } from "@/components/dithercn/dither-image";
 
export function Cover() {
  return (
    <DitherImage
      src="/photos/launch.jpg"
      alt="The launch pad at dawn"
      preset="duotone"
      color="blue"
      className="aspect-video w-full rounded-xl"
    />
  );
}

The default duotone preset suits most photos. Pick another preset, then override any part of it. The image fills its box like object-fit: cover; give the box a width and a height or aspect ratio, or set ratio so the space is reserved before the image loads.

Every cell is a whole number of device pixels and the canvas is scaled up with image-rendering: pixelated, so edges stay sharp at any zoom. Before dithering, the tones are stretched to fill the palette and edges get a light sharpen, which keeps faces and horizons readable at a 2px cell. Turn these off with normalize={false} and sharpen={0}.

While the file loads the box shows a faint dotted field. When it arrives the image resolves from 16× blocks to full detail in about a third of a second.

Examples

Presets

PresetLook
inkAtkinson diffusion, one ink on the page colour. Follows the theme.
duotoneBlue noise over the page colour, a tint and the ink. Follows the theme.
newsprintAn ordered 8×8 screen, near black on warm paper, in both themes.
gameboyFour greens on a 3px Bayer grid.
risoNavy, blue and pink on cream, like a two-drum print.

Palettes from colour names

color takes any palette name: blue, purple, green, pink, orange, red, grey or ink. Each is tuned for light and dark. Here the duotone preset gives each mission its own hue.

For full control, pass palette with two to four colours. Names, CSS variables and CSS colours all work: palette={["--dither-paper", "purple", "oklch(0.9 0.05 300)"]}.

Newsprint article

A fixed palette ignores the theme, so the newsprint card looks the same on a black page and a white one.

Props

PropTypeDefault
src*

Image URL. Images from another origin need CORS headers, because the pixels are read from a canvas.

Type: stringDefault –
alt*

Describes the image for screen readers. Pass "" when it is decorative.

Type: stringDefault –
preset

A starting look: method, cell, contrast and palette.

Type: "ink" | "duotone" | "newsprint" | "gameboy" | "riso"Default "duotone"
color

The ink for the ink and duotone presets. A palette name or any CSS colour.

Type: DitherColorDefault "ink"
palette

Two to four colours that replace the preset palette. Sorted dark to light. Use "transparent" to let the background show through.

Type: [DitherColor, DitherColor, ...DitherColor[]]Default From the preset
method

The dither algorithm.

Type: "bayer4" | "bayer8" | "blue-noise" | "floyd-steinberg" | "atkinson" | "sierra-lite"Default From the preset
cell

Size of one cell in CSS pixels, rounded to whole device pixels.

Type: numberDefault From the preset (2 or 3)
contrast

1 leaves contrast alone. Higher values push tones apart.

Type: numberDefault From the preset
brightness

Added to every tone, from -1 to 1.

Type: numberDefault 0
normalize

Stretch the image's darkest and lightest tones to the ends of the palette.

Type: booleanDefault true
sharpen

Edge sharpening before dithering, 0 to 1.

Type: numberDefault 0.3
invert

Swap light and dark.

Type: booleanDefault false
fit

How the image fills its box, as in object-fit.

Type: "cover" | "contain"Default "cover"
ratio

Width over height. Reserves space before the image loads. A height in className wins.

Type: numberDefault The image's ratio
resolve

Resolve from coarse blocks to full detail the first time the image appears.

Type: booleanDefault true

Notes

  • The root has role="img" and your alt text as its label, and aria-busy while it loads. With alt="" it is hidden from assistive technology.
  • data-state on the root is idle, loading, resolving, ready or error.
  • The image loads when it comes within about 300px of the viewport. It redraws when the box resizes, the page zooms or the theme changes, without resolving again.
  • Under reduced motion the image appears at full detail with no resolve.
  • Dithering runs on the main thread at one pixel per cell. A 1200 by 800 pixel box at a 2px cell is 240,000 pixels, a few milliseconds of work. Coarse frames are much cheaper.