Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/scramble-text.jsonUsage
import { ScrambleText } from "@/components/asciicn/scramble-text";
export function Headline() {
return (
<ScrambleText as="h2" glyphs="katakana" className="text-4xl font-bold">
Decrypting the signal
</ScrambleText>
);
}Every character starts on a random glyph from the set, changes rate times a second, and settles on its letter after duration seconds plus stagger seconds per character before it, so the text resolves left to right. Spaces stay spaces.
The final text is always laid out and the scrambling glyph is painted over each letter's slot, centred on it. Nothing the animation does can move the layout, even in a proportional font or with glyphs wider than the letters they cover.
Examples
Hero
Two lines that play once on mount: a binary eyebrow and a braille headline.
Navigation on hover
trigger="hover" replays a short CP437 scramble each time the pointer enters a link.
Live status
Changing the text plays the scramble again, so a status label can decode into each new state. The live region announces the settled word.
Props
| Prop | Type | Default |
|---|---|---|
| children The final text. It is what screen readers, search and copy get. | Type: string | Default – |
| as The element to render. | Type: "h1" | "h2" | "h3" | "h4" | "p" | "span" | "div" | Default "span" |
| glyphs The glyph set characters cycle through. | Type: "katakana" | "braille" | "cp437" | "petscii" | "blocks" | "symbols" | "hex" | "binary" | Default "katakana" |
| characters Your own glyphs to cycle through. Wins over glyphs. | Type: string | Default – |
| trigger When it plays. loop plays again after pause seconds. Changing the text always plays it. | Type: "mount" | "view" | "hover" | "loop" | Default "view" |
| duration Seconds every character scrambles before the first one settles. | Type: number | Default 0.5 |
| stagger Seconds between neighbours settling, left to right. | Type: number | Default 0.035 |
| delay Seconds to wait before starting. | Type: number | Default 0 |
| rate New glyphs per second while scrambling. | Type: number | Default 20 |
| pause loop only: seconds to hold the settled text before playing again. | Type: number | Default 2.5 |
| color Colour of the scrambling glyphs. Settled letters keep the text colour. | Type: AsciiColor | Default "ink" |
| onComplete Called each time the text has fully settled. | Type: () => void | Default – |
Other props pass through to the element.
Notes
- Screen readers get the final text once, from a visually hidden copy. The animated letters are
aria-hidden. Copying the visible text copies the final string, never the glyphs, because the glyphs are drawn by CSS pseudo-elements. - With reduced motion, nothing scrambles: the text shows as is.
viewplays when the text scrolls into view. Leaving the screen mid-scramble settles every letter at once.- Katakana and some CP437 glyphs come from a fallback font when the glyph font lacks them. They are centred over each letter, so a wider glyph never pushes its neighbours.
- One
requestAnimationFrameloop runs while letters are scrambling and stops when they settle. React does not re-render per frame.