# Ripples

Concentric rings spreading from one to four points and crossing like drops on still water, 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/ripples.json](https://dithercn.dev/r/ripples.json), under `files[].content`.

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { Ripples } from "@/components/dithercn/ripples";

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

Each point in `sources` sends out rings that fade with distance. A lone ring stops at the middle colour; where rings from two points cross, they add up to full ink, so the interference pattern shows up as bright knots. A slow swell bends the rings slightly so they don't look drawn with a compass.

## Examples [#examples]

### One drop [#one-drop]

A single source just below the frame sends rings up the box. They fade out before they reach the text at the top, and a little less `intensity` keeps the nearest ones soft.

```tsx title="examples/ripples-drop.tsx"
import { Ripples } from "@/components/dithercn/ripples";

export default function RipplesDrop() {
  return (
    <section className="relative isolate flex size-full flex-col items-center justify-start gap-3 px-6 pt-14 text-center sm:pt-20">
      <Ripples
        sources={[[0.5, 1.02]]}
        rings={9}
        decay={1.4}
        intensity={0.85}
        className="absolute inset-0 -z-10"
      />
      <h3 className="max-w-md text-3xl font-medium tracking-[-0.03em] text-balance">
        Every change spreads
      </h3>
      <p className="text-muted-foreground max-w-sm text-sm text-pretty">
        One source just below the frame sends rings up towards the text and
        fades before it reaches it.
      </p>
    </section>
  );
}
```

### Rain [#rain]

Four sources, tighter rings and a faster `decay` through the chart colours. The 8×8 matrix gives the fading rings a finer grain.

```tsx title="examples/ripples-rain.tsx"
import { Ripples } from "@/components/dithercn/ripples";

export default function RipplesRain() {
  return (
    <Ripples
      sources={[
        [0.2, 0.3],
        [0.55, 0.7],
        [0.82, 0.25],
        [0.35, 0.92],
      ]}
      rings={14}
      decay={2}
      matrix="bayer8"
      palette={["--dither-paper", "--chart-3", "--chart-2", "--chart-1"]}
      className="absolute inset-0"
    />
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `sources` | `[x: number, y: number][]` | `[[0.28, 0.38], [0.74, 0.66]]` | Where the rings start, as fractions of the box from the top left. One to four points; values outside 0 to 1 put a point out of frame. |
| `rings` | `number` | `12` | How many rings fit in the height of the box. |
| `decay` | `number` | `1` | How quickly the rings fade away from their source. 0 keeps them strong across the box. |
| `intensity` | `number` | `1` | The brightest tone, from 0 to 1. Lower it to put 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. |
| `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 the rings travel outwards. |
| `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 rings fill 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 rings drawn with CSS gradients.