# Halftone

A print halftone: round dots on a rotated screen that swell and shrink with a source, in one ink or up to three overlapping like CMYK.

> For the complete documentation index, see [llms.txt](/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](/.well-known/agent-skills/dithercn/SKILL.md).

<ComponentPreview src="examples/halftone-demo.tsx" size="full" align="stretch" controls="halftoneControls">
  <HalftoneDemo />
</ComponentPreview>

## Installation [#installation]

<InstallBlock name="halftone" />

## Usage [#usage]

```tsx
import { Halftone } from "@/components/dithercn/halftone";

export function Hero() {
  return (
    <section className="relative isolate min-h-[480px]">
      <Halftone color="blue" className="absolute inset-0 -z-10" />
      <h1>Your headline</h1>
    </section>
  );
}
```

Each square of a rotated screen holds one round dot, sized by the source at its centre, the way a printing screen works. Dot edges are dithered at the screen's own device pixels, so dots stay round and grow smoothly from a speck to touching their neighbours. The screen slides slowly along its angle (`drift`), so dots swell and shrink as they cross the source.

Pass `channels` instead of `color` for two or three inks. Each gets its own screen, turned 30° from the last, and overlapping dots mix their colours. With blue, pink and orange you get the rosettes of a CMYK print.

## Examples [#examples]

### Spotlight behind a headline [#spotlight-behind-a-headline]

`source="radial"` grows the dots towards `center`. With the centre below the bottom edge, a `radius` under 1 and a transparent background, the dots swell up from the bottom of the section and run out before the text.

<ComponentPreview src="examples/halftone-spotlight.tsx" size="lg" align="stretch">
  <HalftoneSpotlight />
</ComponentPreview>

### Duotone ramp in a card [#duotone-ramp-in-a-card]

`source="linear"` grows the dots towards `direction`. Two channels at a shallow 15° screen, held still, and faded in from the left with a CSS mask.

<ComponentPreview src="examples/halftone-card.tsx">
  <HalftoneCard />
</ComponentPreview>

### Three-ink print [#three-ink-print]

A coarse 16px screen in three inks behind an editorial block.

<ComponentPreview src="examples/halftone-print.tsx" size="full" align="stretch">
  <HalftonePrint />
</ComponentPreview>

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `DitherColor` | `"ink"` | Colour of the dots: a palette name such as "blue", a CSS variable name, or any CSS colour. |
| `channels` | `[DitherColor, DitherColor, DitherColor?]` | `–` | Two or three inks on separate screens, 30° apart. Overlaps mix. Use instead of color. |
| `background` | `DitherColor` | `"--dither-paper"` | Colour behind the dots. "transparent" lays the screen over the content underneath. |
| `source` | `"noise" \| "radial" \| "linear"` | `"noise"` | What sizes the dots: soft drifting clouds, distance from a point, or a ramp. |
| `center` | `[x: number, y: number]` | `[0.5, 0.5]` | Radial only. Where the dots are largest, as fractions of the box from the top left. |
| `radius` | `number` | `1` | Radial only. Where the dots run out. 1 reaches the farthest corner. |
| `direction` | `number` | `0` | Linear only. Direction the dots grow towards, in degrees as in CSS. |
| `size` | `number` | `10` | Distance between dot centres in CSS pixels. |
| `angle` | `number` | `45` | Screen angle in degrees. Each extra channel adds 30°. |
| `intensity` | `number` | `0.9` | Size of the largest dot, 0 to 1. At 1 the biggest dots just touch. |
| `scale` | `number` | `1` | Noise only. Size of the clouds. |
| `drift` | `number` | `1` | How fast the screen slides along its angle. 0 holds the screen still. |
| `speed` | `number` | `1` | How fast the source moves. |
| `animate` | `boolean` | `true` | false draws one still frame. |
| `cell` | `number` | `one device pixel` | Size of one rendered pixel in CSS pixels. Larger cells give blockier dots. |
| `className` | `string` | `–` | Size and position. The halftone fills this box. |

## Notes [#notes]

* The halftone 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 shows a CSS dot screen in the same inks.
* Overlap colours are mixed with CSS `color-mix` in oklch, so they follow the theme along with the inks.