# Waves

Layered swells under a glowing horizon, or thin lines like a sound wave, drawn as a dither.

> 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/waves.json](https://dithercn.dev/r/waves.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://dithercn.dev/r/waves.json
```

## Usage [#usage]

```tsx
import { Waves } from "@/components/dithercn/waves";

export function Footer() {
  return (
    <footer className="relative isolate min-h-[320px]">
      <Waves className="absolute inset-0 -z-10" />
    </footer>
  );
}
```

Rows of swells roll towards you below the horizon. Distant rows are small and bright, near ones large and dark, and every crest has a one-cell highlight.

## Examples [#examples]

### Dusk [#dusk]

A lower `horizon`, eight `layers` and a warm palette give a sea at sunset. A smaller `amplitude` calms the water.

```tsx title="examples/waves-dusk.tsx"
import { Waves } from "@/components/dithercn/waves";

export default function WavesDusk() {
  return (
    <Waves
      horizon={0.5}
      layers={8}
      amplitude={0.7}
      speed={0.6}
      matrix="bayer8"
      cell={4}
      palette={[
        "--dither-paper",
        "oklch(0.35 0.1 300)",
        "oklch(0.58 0.17 20)",
        "oklch(0.8 0.14 60)",
      ]}
      className="absolute inset-0"
    />
  );
}
```

### Sound wave [#sound-wave]

`variant="lines"` draws thin lines that swell in the middle and taper at both ends. It suits audio players, voice input and anything that listens.

```tsx title="examples/waves-lines.tsx"
import { Waves } from "@/components/dithercn/waves";

export default function WavesLines() {
  return (
    <div className="bg-background border-border relative flex w-full max-w-sm [--dither-paper:var(--background)] flex-col gap-4 rounded-2xl border p-5">
      <div className="flex items-center justify-between text-sm">
        <span className="font-medium">Episode 42: Cells</span>
        <span className="text-muted-foreground font-mono text-xs tabular-nums">
          12:08
        </span>
      </div>
      <div className="relative h-24 overflow-hidden rounded-lg">
        <Waves
          variant="lines"
          layers={4}
          cell={2}
          className="absolute inset-0"
        />
      </div>
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"ocean" \| "lines"` | `"ocean"` | Swells under a horizon, or thin lines like a sound wave. |
| `horizon` | `number` | `0.62` | Ocean only. Height of the horizon, from 0 (bottom) to 1 (top). |
| `layers` | `number` | `6` | How many waves, from 2 to 8. |
| `amplitude` | `number` | `1` | Height of the waves. 1 is calm, 2 is rough. |
| `palette` | `[DitherColor, DitherColor, ...DitherColor[]]` | `Paper, then tints of --dither-ink` | Colours from paper to ink: palette names such as "blue", CSS variable names, or any CSS colour. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | The threshold pattern. See Dither engine. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |
| `speed` | `number` | `1` | How fast it moves. |
| `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 background 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 a CSS approximation in the same colours.
* It costs one small draw per frame at 30 fps, at one pixel per dither cell. See [Performance](/docs/engine/performance).