# Metaballs

Soft blobs that drift, merge and split like a lava lamp, 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).

<ComponentPreview src="examples/metaballs-demo.tsx" size="full" align="stretch" controls="metaballsControls">
  <MetaballsDemo />
</ComponentPreview>

## Installation [#installation]

<InstallBlock name="metaballs" />

## Usage [#usage]

```tsx
import { Metaballs } from "@/components/dithercn/metaballs";

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

Each blob rises and sinks on its own slow loop. Where two come close, their edges reach for each other and join, then pull apart again. Every blob is densest at its core and has a soft halo around its edge.

## Examples [#examples]

### Contour rings [#contour-rings]

`variant="rings"` draws contour lines around the blobs instead of filling them. The rings merge and split with the blobs.

<ComponentPreview src="examples/metaballs-rings.tsx" size="lg" align="stretch">
  <MetaballsRings />
</ComponentPreview>

### Lava lamp [#lava-lamp]

Five blobs in warm colours, sped up a little, in a tall rounded box. In a narrow box the blobs are sized by its width.

<ComponentPreview src="examples/metaballs-lamp.tsx">
  <MetaballsLamp />
</ComponentPreview>

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"solid" \| "rings"` | `"solid"` | Shaded blobs with a soft halo, or contour lines around them. |
| `count` | `number` | `5` | How many blobs, from 2 to 8. |
| `size` | `number` | `1` | Size of the blobs. In a tall box they are sized by the width. |
| `palette` | `[DitherColor, DitherColor, ...DitherColor[]]` | `["--dither-paper", "--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 draws one still frame. |
| `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 soft CSS blobs 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).