# Glyph rain

Falling glyphs gather into a message, then fade and form again.

> 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 asciicn agent skill, for building any ASCII effect on the engine, is at [/.well-known/agent-skills/asciicn/SKILL.md](/.well-known/agent-skills/asciicn/SKILL.md).

> **This is an example, not the only option.** It is one component built on the asciicn 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 `AsciiShader` ([Write your own](/docs/engine/write-your-own.md)), or draw a CPU grid with `AsciiGrid`, and read this example's source for technique. Its full source is in [https://asciicn.dev/r/glyph-rain.json](https://asciicn.dev/r/glyph-rain.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://asciicn.dev/r/glyph-rain.json
```

## Usage [#usage]

```tsx
import { GlyphRain } from "@/components/asciicn/glyph-rain";

export function Hero() {
  return (
    <section className="relative h-[480px]">
      <GlyphRain message="asciicn" className="absolute inset-0" />
      <h1 className="sr-only">asciicn</h1>
    </section>
  );
}
```

Each stream is one column of glyphs with a head and a trail. The head is drawn in `accent`, the cell or two behind it shade from the accent into `color`, and the rest of the trail fades out. Glyphs come from the charset in order of ink: dense ones near the head, faint ones at the tail. Every cell keeps its own glyph, and now and then one changes, so a trail flickers a little as it passes.

Without `message` the rain is ambient: streams start at random columns, fall at different speeds and start again at the top.

With `message`, the text is set in a built-in pixel font and scaled to fill most of the box. Each font pixel covers a whole number of quadrants, so every edge lands on a cell boundary and the letters stay crisp. When a stream's head crosses a cell of the message, that cell fills in behind it: first a dense glyph in the accent, then `░ ▒ ▓`, then a solid block. While the message forms, most new streams fall through its columns, so you watch the letters being written. Cells no stream reaches fill in on their own after a few seconds.

The message holds for `hold` seconds, then wears away cell by cell, faster where a stream passes, and forms again after a short rest. With `loop={false}` it forms once and stays. Trails behind the message are dimmed so the letters read.

The font covers A to Z in both cases, the digits and `! " # & ' ( ) * + , - . / : < = > ? @ [ ] _ |`. Other characters draw as `?`.

## Examples [#examples]

### Headline over a hero [#headline-over-a-hero]

The message is the headline. Keep it short: four to eight characters fill a hero at any width. Put the same words in a real heading for screen readers.

```tsx title="examples/glyph-rain-hero.tsx"
import { GlyphRain } from "@/components/asciicn/glyph-rain";
import { Button } from "@/components/ui/button";

export default function GlyphRainHero() {
  return (
    <section className="flex w-full flex-col items-center gap-5 text-center">
      <div className="relative h-56 w-full sm:h-72">
        <GlyphRain message="v1.0" hold={6} className="absolute inset-0" />
      </div>
      <h3 className="sr-only">Version 1.0</h3>
      <p className="max-w-md text-pretty text-muted-foreground text-sm">
        Faster builds, a smaller runtime and a new plugin API. Upgrading takes
        one command.
      </p>
      <div className="flex gap-2">
        <Button size="sm">Read the notes</Button>
        <Button size="sm" variant="outline">
          Changelog
        </Button>
      </div>
    </section>
  );
}
```

### Ambient background [#ambient-background]

No `message`, a lower `density` and `speed`, and `charset="braille"`, so each trail thins from full cells to single dots. The content sits on its own panel above the rain.

```tsx title="examples/glyph-rain-ambient.tsx"
import { GlyphRain } from "@/components/asciicn/glyph-rain";

export default function GlyphRainAmbient() {
  return (
    <section className="relative isolate flex h-[380px] w-full items-center justify-center overflow-hidden px-6">
      <GlyphRain
        charset="braille"
        density={0.35}
        speed={0.7}
        className="absolute inset-0 -z-10"
      />
      <div className="max-w-sm rounded-2xl border bg-card/90 px-7 py-6 text-center backdrop-blur-sm">
        <p className="font-mono text-muted-foreground text-xs uppercase tracking-widest">
          Night shift
        </p>
        <h3 className="mt-2 text-balance font-medium text-2xl tracking-tight">
          Deploys keep running while you sleep
        </h3>
      </div>
    </section>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `message` | `string` |  | Text the rain settles into, in block letters. Line breaks start new lines. Without it the rain is ambient. |
| `density` | `number` | `0.5` | Share of columns with a stream falling at once, from 0 to 1. |
| `speed` | `number` | `1` | Fall speed multiplier. |
| `hold` | `number` | `5` | Seconds the finished message holds before it wears away. |
| `loop` | `boolean` | `true` | Wear the message away and form it again, forever. false forms it once and keeps it. |
| `charset` | `"latin" \| CharsetName` | `"latin"` | Glyphs the rain falls in. latin is digits, letters and a few symbols. Any engine charset works, such as katakana or braille. |
| `chars` | `string` |  | Your own glyphs, faint to dense, such as ".:+*#". Wins over charset. |
| `colorMode` | `"truecolor" \| "mono" \| "duotone"` | `"truecolor"` | truecolor: trails in color, heads and message in accent. mono: everything in color or the phosphor. duotone: by brightness along ramp. |
| `color` | `AsciiColor` | `"--ascii-ink"` | Trail colour, and every glyph in mono. |
| `accent` | `AsciiColor` | `"orange"` | Heads and the message in truecolor. The palette's amber. |
| `phosphor` | `PhosphorName` |  | mono: a CRT phosphor for the ink and the glass behind it. |
| `ramp` | `RampName \| string[]` | `"sodium"` | duotone: a ramp name or stops as #rrggbb. |
| `background` | `"paper" \| "transparent"` | `"transparent"` | transparent shows the page between glyphs. A phosphor or duotone paints its own glass unless this is transparent. |
| `fx` | `PostFx` |  | CRT effects, drawn as a CSS overlay. |
| `cell` | `number` | `12` | Glyph cell height in CSS pixels. |
| `fps` | `number` | `30` | Frame rate cap. |
| `animate` | `boolean` | `true` | false holds one still frame with the message complete. |
| `handle` | `RefObject<AsciiHandle \| null>` |  | Filled with a handle for export. |

## Notes [#notes]

* Each column falls at its own speed, with an amber head and a trail that fades through the charset. A column that crosses a cell of the message leaves that cell lit, so the words assemble out of the rain, hold, wear away and form again.
* The canvas is decorative and hidden from assistive technology. Put the message in a real heading too, visible or `sr-only`.
* The first frame already shows the message part written, so it never opens on a blank box.
* Under reduced motion it draws one still frame: the finished message with rain around it. It pauses offscreen and in hidden tabs.
* After a resize the message comes back whole at its new size.
* A message too wide for the box at the smallest size runs off both sides. Use shorter words or a line break on narrow screens.