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

Image compare

A draggable before and after slider: the dithered image on one side, the original or another dither on the other.

Controls
Dithered side
Ink
Cell2px
Labels

Installation

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

Usage

import { ImageCompare } from "@/components/dithercn/image-compare";
 
export function Showcase() {
  return (
    <ImageCompare
      src="/photos/launch.jpg"
      alt="The launch pad at dawn, dithered and original"
      className="w-full rounded-xl"
    />
  );
}

Drag the divider, click anywhere on the image to send it there, or tab to the handle and use the arrow keys. By default the left side is dithered with the duotone preset and the right side is the original photo. When the image first comes into view, the divider nudges left and right once so readers see that it moves.

Each side is either { kind: "original" } or { kind: "dither", ...look }, where the look takes the same props as Dither image: preset, color, palette, method, cell and the rest.

Examples

Two dither looks

Both sides can be dithered. Here newsprint meets Game Boy greens.

Feature section

A landing page block that shows what the brand treatment does to a photo.

Props

PropTypeDefault
src*

Image URL. Cross-origin images need CORS headers.

Type: stringDefault –
alt*

Describes the image and the comparison. Used as the group's label.

Type: stringDefault –
before

The left side.

Type: { kind: "original" } | { kind: "dither", ...DitherLook }Default { kind: "dither" }
after

The right side.

Type: { kind: "original" } | { kind: "dither", ...DitherLook }Default { kind: "original" }
beforeLabel

Label on the left side, also used in the slider's name.

Type: stringDefault "Dithered" or "Original"
afterLabel

Label on the right side.

Type: stringDefault "Dithered" or "Original"
labels

Show the corner labels. They fade out when the divider covers them.

Type: booleanDefault true
value

Divider position from the left, 0 to 100. Makes the slider controlled.

Type: numberDefault –
defaultValue

Starting position when uncontrolled.

Type: numberDefault 50
onValueChange

Called on every move, from pointer or keyboard.

Type: (value: number) => voidDefault –
hint

Nudge the divider once when the image first comes into view.

Type: booleanDefault true
ratio

Width over height.

Type: numberDefault The image's ratio
fit

How the image fills its box, on both sides.

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

Notes

  • The handle is a role="slider" with a value from 0 to 100. Arrow keys move it by 1, Shift and arrow or Page Up and Page Down by 10, Home and End to the ends.
  • Clicks glide the divider over 200ms; drags and key presses follow exactly.
  • On touch screens a sideways drag moves the divider and a vertical swipe still scrolls the page.
  • Under reduced motion there is no hint and no glide.