# Glitch text

Text that glitches in short bursts, with slices jumping sideways, letters swapped for block and box glyphs and a colour split, then snaps back clean.

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

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { GlitchText } from "@/components/asciicn/glitch-text";

export function Headline() {
  return (
    <GlitchText as="h2" intensity={0.4} className="text-5xl font-bold">
      Signal lost
    </GlitchText>
  );
}
```

A burst lasts `duration` seconds and steps through a new frame about every 55 ms. Each frame cuts the text into seven horizontal bands at random heights and shifts some of them sideways, swaps a few letters for glyphs from `glyphs`, and pulls two coloured ghosts apart behind the text. A few frames in every burst are clean, so the text flickers rather than smears. When the burst ends, the text is exactly as it was.

`intensity` scales all three at once. At the default 0.35 a burst is a quick twitch: small slips, one or two swapped letters and a thin colour fringe. At 1 the bands jump half a letter or more, close to half the letters turn to blocks, and the ghosts spread wide.

## Examples [#examples]

### Links on hover [#links-on-hover]

`trigger="hover"` plays one short burst each time the pointer enters a link. Box glyphs suit thin monospace labels.

```tsx title="examples/glitch-text-hover.tsx"
import { GlitchText } from "@/components/asciicn/glitch-text";

const LINKS = ["Overview", "Components", "Engine", "Changelog"] as const;

export default function GlitchTextHover() {
  return (
    <nav aria-label="Product">
      <ul className="flex flex-wrap justify-center gap-x-8 gap-y-3 font-mono text-sm">
        {LINKS.map((label) => (
          <li key={label}>
            <a
              href="#top"
              className="rounded-sm text-muted-foreground outline-offset-4 hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring"
            >
              <GlitchText
                trigger="hover"
                glyphs="box"
                intensity={0.6}
                duration={0.25}
              >
                {label}
              </GlitchText>
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}
```

### Full intensity [#full-intensity]

An error banner at `intensity={1}` with block glyphs, a longer burst and orange and purple ghosts. The eyebrow above it runs at half strength.

```tsx title="examples/glitch-text-meltdown.tsx"
import { GlitchText } from "@/components/asciicn/glitch-text";

export default function GlitchTextMeltdown() {
  return (
    <div className="flex max-w-2xl flex-col items-start gap-2 font-mono">
      <GlitchText
        as="p"
        intensity={0.5}
        interval={1.5}
        split={["orange", "purple"]}
        className="text-xs tracking-widest text-muted-foreground uppercase"
      >
        kernel panic · core 3
      </GlitchText>
      <GlitchText
        as="h3"
        glyphs="blocks"
        intensity={1}
        interval={1.2}
        duration={0.6}
        className="text-3xl font-bold tracking-tight sm:text-5xl"
      >
        SEGMENT 0x7F3A CORRUPTED
      </GlitchText>
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `string` |  | The text. It is what screen readers, search and copy get. |
| `as` | `"h1" \| "h2" \| "h3" \| "h4" \| "p" \| "span" \| "div"` | `"span"` | The element to render. |
| `intensity` | `number` | `0.35` | How hard a burst hits, 0 to 1: how far slices move, how many letters swap and how far the colours split. |
| `interval` | `number` | `3` | auto only: average seconds between bursts. Each wait varies by up to a third either way. |
| `duration` | `number` | `0.4` | Seconds one burst lasts. |
| `trigger` | `"auto" \| "hover" \| "inView"` | `"auto"` | auto bursts on a timer while the text is on screen, hover once per pointer enter, inView once each time it scrolls into view. |
| `glyphs` | `"mixed" \| "blocks" \| "box" \| "shades"` | `"mixed"` | The glyph set swapped-in letters come from. |
| `characters` | `string` |  | Your own glyphs to swap in. Wins over glyphs. |
| `split` | `[AsciiColor, AsciiColor]` | `["red", "blue"]` | Colours of the two ghosts that split left and right during a burst. |

Other props pass through to the element.

## Notes [#notes]

* Screen readers get the text once, from a visually hidden copy. The visible layers are `aria-hidden`. Selecting and copying the visible text copies the real string, because swapped glyphs are drawn by CSS pseudo-elements.
* Every effect is a transform, a clip or a pseudo-element, so the text never changes size and nothing around it moves. Slices can reach past the element's edges during a burst; give the parent `overflow: hidden` if that matters.
* With reduced motion there are no bursts: the text shows as is.
* `auto` stops while the text is offscreen or the tab is hidden, and the first burst comes half a second after the text appears.
* No React render per frame. A burst sets styles on its layers directly and stops its timer when it ends.