# Flow field

A wind map drawn as a dither: streamlines of a slowly turning, swirling flow, with bright streaks that travel along them and leave fading tails.

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

> **This is an example, not the only option.** It is one component built on the dithercn engine. Install it when it is what you were asked for. For any other look, don't stretch its props to fit: write your own field on `DitherShader` ([Write your own dither](/docs/engine/write-your-own.md)), and read this example's source for technique. Its full source is in [https://dithercn.dev/r/flow-field.json](https://dithercn.dev/r/flow-field.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://dithercn.dev/r/flow-field.json
```

## Usage [#usage]

```tsx
import { FlowField } from "@/components/dithercn/flow-field";

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

The wind is a steady breeze along `angle`, bent by slow swirls that drift downwind and change shape as they go. The streamlines are the level lines of its stream function, so they never cross, and they bunch together where the wind runs fast. Bright streaks travel along each line at their own pace, with a crisp head and a tail that fades behind it. Streaks in fast wind are brighter than those in still air, and the prevailing direction veers a few degrees back and forth over time. The swirls keep their size in pixels, so a narrow box shows a smaller piece of the same wind rather than a squashed one.

## Examples [#examples]

### Weather map [#weather-map]

`variant="lines"` draws every streamline in the first colour after the paper, and the streaks run through the rest of the palette as they brighten, so the colour reads as wind speed. More `density` and `turbulence`, and a higher `scale` for smaller swirls, fill the box with eddies, and `levels={19}` puts each of the four colours on an exact step.

```tsx title="examples/flow-field-weather.tsx"
import { FlowField } from "@/components/dithercn/flow-field";
import { cssColor } from "@/lib/dither";
import type { DitherPalette } from "@/lib/dither";

// Paper, then the streamlines, then the streaks from light wind to strong.
const PALETTE = [
  "--dither-paper",
  "color-mix(in oklch, var(--dither-color-blue) 55%, var(--dither-paper))",
  "green",
  "orange",
] as const satisfies DitherPalette;

const KEY = [
  { color: PALETTE[1], label: "Light" },
  { color: PALETTE[2], label: "Fresh" },
  { color: PALETTE[3], label: "Strong" },
];

export default function FlowFieldWeather() {
  return (
    <section className="relative isolate size-full">
      <FlowField
        variant="lines"
        density={1.15}
        turbulence={1.15}
        scale={1.15}
        trail={0.7}
        angle={105}
        cell={2}
        levels={19}
        palette={PALETTE}
        className="absolute inset-0 -z-10"
      />
      <figure className="bg-background/85 absolute bottom-4 left-4 flex flex-col gap-2 rounded-lg border px-3 py-2.5 backdrop-blur-sm">
        <figcaption className="text-muted-foreground font-mono text-[10px] tracking-[0.08em] uppercase">
          Surface wind
        </figcaption>
        <ul className="flex gap-3 text-xs">
          {KEY.map(({ color, label }) => (
            <li key={label} className="flex items-center gap-1.5">
              <span
                aria-hidden="true"
                className="size-2 rounded-full"
                style={{ backgroundColor: cssColor(color) }}
              />
              {label}
            </li>
          ))}
        </ul>
      </figure>
    </section>
  );
}
```

### Behind a headline [#behind-a-headline]

Fewer lines, a low `turbulence` and long trails give a calm, even breeze. Two soft tints of the ink keep the brightest streak well below full contrast, so the text reads on top.

```tsx title="examples/flow-field-headline.tsx"
import { FlowField } from "@/components/dithercn/flow-field";

export default function FlowFieldHeadline() {
  return (
    <section className="relative isolate flex size-full flex-col items-start justify-center gap-3 px-6 sm:px-10">
      <FlowField
        density={0.7}
        turbulence={0.5}
        trail={0.8}
        angle={82}
        speed={0.6}
        palette={[
          "--dither-paper",
          "color-mix(in oklch, var(--dither-ink) 22%, var(--dither-paper))",
          "color-mix(in oklch, var(--dither-ink) 45%, var(--dither-paper))",
        ]}
        className="absolute inset-0 -z-10"
      />
      <h3 className="max-w-md text-3xl font-medium tracking-[-0.03em] text-balance">
        Deploys that go where the traffic goes
      </h3>
      <p className="text-muted-foreground max-w-sm text-sm text-pretty">
        Fewer lines, a gentle wind and two soft tints of the ink keep the
        streaks quiet behind the text.
      </p>
    </section>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"streaks" \| "lines"` | `"streaks"` | streaks draws only the travelling dashes. lines also draws every streamline underneath them. |
| `density` | `number` | `1` | How tightly the streamlines pack. 2 packs twice as many in. |
| `trail` | `number` | `0.55` | Length of each streak's fading tail, as a share of the gap between streaks, from 0.1 to 1. |
| `turbulence` | `number` | `1` | How much the wind meanders and curls into eddies, from 0 (a straight, steady wind) to 2. |
| `scale` | `number` | `1` | Size of the swirls. Higher values pack more, smaller ones in. |
| `angle` | `number` | `70` | Direction the wind blows towards, in degrees as in CSS: 0 points up, 90 right. |
| `intensity` | `number` | `1` | The brightest tone, from 0 to 1. Lower it to sit text on top. |
| `palette` | `[DitherColor, DitherColor, ...DitherColor[]]` | `Paper, a tint of --dither-ink, then --dither-ink` | Colours from paper to ink: palette names such as "blue", CSS variable names, or any CSS colour. Streamlines take the second colour; streaks brighten through the rest. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer8"` | The threshold pattern. See Dither engine. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |
| `speed` | `number` | `1` | How fast the wind blows and the swirls drift. |
| `animate` | `boolean` | `true` | false stops the motion: a still frame if it starts off, the current frame if it was moving. |
| `className` | `string` | `–` | Size and position. The wind fills this box. |

## Notes [#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 shows straight CSS lines along the wind in the same colours, cut into dashes, spaced by `density` and with tails as long as `trail` sets.
* It samples the stream function three times per cell, at 30 fps and one pixel per dither cell. See [Performance](/docs/engine/performance).