# Starfield

Dithered stars in three parallax layers that twinkle and drift slowly, for space themes, sign-in pages and launch pages.

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

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { Starfield } from "@/components/dithercn/starfield";

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

Stars sit on three grids of different sizes. Far stars are small, dim and slow; near stars are brighter, move faster and the brightest get a small cross. A faint haze of dithered dust drifts behind them.

Each star snaps to one palette colour, so it never flickers between dither thresholds as it moves. The last palette colour is the brightest star, the middle ones are dimmer stars and the haze.

## Examples [#examples]

### Behind a sign-in form [#behind-a-sign-in-form]

A slower drift to the lower left and a stronger haze. The form has a translucent background so stars show through its edges.

```tsx title="examples/starfield-sign-in.tsx"
import { Starfield } from "@/components/dithercn/starfield";

export default function StarfieldSignIn() {
  return (
    <div className="relative isolate flex size-full items-center justify-center p-6">
      <Starfield
        angle={200}
        drift={1}
        nebula={0.8}
        className="absolute inset-0 -z-10"
      />
      <form className="bg-background/80 flex w-full max-w-xs flex-col gap-3 rounded-2xl border p-5 backdrop-blur-sm">
        <div className="flex flex-col gap-1">
          <h3 className="text-base font-medium">Sign in to Orbit</h3>
          <p className="text-muted-foreground text-sm">
            Use the email you signed up with.
          </p>
        </div>
        <label className="flex flex-col gap-1.5 text-sm">
          Email
          <input
            type="email"
            name="email"
            autoComplete="email"
            placeholder="you@example.com"
            className="bg-background focus-visible:ring-ring h-9 rounded-lg border px-3 text-sm outline-none focus-visible:ring-2"
          />
        </label>
        <button
          type="button"
          className="bg-primary text-primary-foreground focus-visible:ring-ring h-9 rounded-lg text-sm font-medium outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
        >
          Continue
        </button>
      </form>
    </div>
  );
}
```

### A quiet sky [#a-quiet-sky]

`drift={0}` holds the stars in place, and `twinkle={1}` lets them pulse fully. No haze, fewer stars, in neutral foreground colours.

```tsx title="examples/starfield-still.tsx"
import { Starfield } from "@/components/dithercn/starfield";

export default function StarfieldStill() {
  return (
    <Starfield
      drift={0}
      twinkle={1}
      nebula={0}
      density={0.6}
      palette={["--dither-paper", "--muted-foreground", "--foreground"]}
      className="absolute inset-0"
    />
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `density` | `number` | `1` | How many stars, from 0 to 2. |
| `twinkle` | `number` | `0.6` | How much each star pulses, from 0 (steady) to 1. |
| `angle` | `number` | `270` | Direction the stars drift, in degrees as in CSS: 0 is up, 90 right. |
| `drift` | `number` | `1.6` | Drift speed of the nearest layer, in cells per second. The others move at 0.6 and 0.3 of it. 0 holds them still. |
| `nebula` | `number` | `0.5` | Strength of the dust haze behind the stars, from 0 to 1. |
| `palette` | `[DitherColor, DitherColor, ...DitherColor[]]` | `["--dither-paper", "--chart-2", "--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"` | `"blue-noise"` | The threshold pattern. See Dither engine. |
| `levels` | `number` | `16 per palette step` | Tones before dithering. The default keeps every palette colour exact so stars stay solid. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |
| `speed` | `number` | `1` | How fast time runs, for both drift and twinkle. |
| `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 starfield fills this box. |

## Notes [#notes]

* The starfield 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 tiled CSS dots.
* Twinkle rates are random per star and slow, between about 3 and 12 seconds a pulse, so nothing flashes.