# Smoke

Soft smoke that billows upward and thins out, lit from above, 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/smoke.json](https://dithercn.dev/r/smoke.json), under `files[].content`.

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { Smoke } from "@/components/dithercn/smoke";

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

The smoke is thickest at the bottom and thins out as it climbs. Light from the upper left catches one side of each plume and leaves the other in shadow. The thickest parts stop short of solid ink, so the dither always shows.

## Examples [#examples]

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

A lower `height` keeps the smoke in the bottom half, and a lower `density` makes it lighter. Negative `wind` blows it to the left.

```tsx title="examples/smoke-headline.tsx"
import { Smoke } from "@/components/dithercn/smoke";

export default function SmokeHeadline() {
  return (
    <section className="relative isolate flex size-full flex-col items-center gap-3 px-6 pt-14 text-center sm:pt-20">
      <Smoke
        height={0.45}
        density={0.8}
        wind={-0.3}
        className="absolute inset-0 -z-10"
      />
      <h3 className="max-w-md text-3xl font-medium tracking-[-0.03em] text-balance">
        Nothing left to burn
      </h3>
      <p className="text-muted-foreground max-w-sm text-sm text-pretty">
        With the height at 0.45 the smoke thins out halfway up and leaves the
        top of the box for text.
      </p>
    </section>
  );
}
```

### Embers [#embers]

Four warm colours turn the smoke into the glow above a fire. Blue noise gives an even grain instead of the Bayer grid.

```tsx title="examples/smoke-embers.tsx"
import { Smoke } from "@/components/dithercn/smoke";

export default function SmokeEmbers() {
  return (
    <Smoke
      scale={1.4}
      height={1}
      wind={0.6}
      matrix="blue-noise"
      palette={[
        "--dither-paper",
        "oklch(0.32 0.08 30)",
        "oklch(0.58 0.19 35)",
        "oklch(0.82 0.15 70)",
      ]}
      className="absolute inset-0"
    />
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `scale` | `number` | `1` | Size of the plumes. Higher values make more, smaller ones. |
| `height` | `number` | `0.8` | How high the smoke climbs before it thins out, from 0 to 1. |
| `wind` | `number` | `0.2` | Sideways drift. Negative blows left, positive right. |
| `density` | `number` | `1` | How thick the smoke is, from 0 to 1.5. |
| `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 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 soft CSS haze 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).