Text Scramble
A monospace headline that decodes character by character out of a cipher of glyphs, plus nav links that scramble on hover.
Live demo
Hover or tab through the nav links to decode them in place. The headline cycles four phrases; the footer counts characters as they lock.
The Prompt
Build a text scramble effect: a headline that decodes character by character out of a cipher of glyphs, cycling through four phrases, plus nav links that scramble on hover. Match my project's existing stack, conventions, and design tokens. If there are none, output a single HTML file with inline CSS and JavaScript and zero dependencies.
The goal: a signal-decode poster. Monospace everything, off-white on ink, one acid-lime accent that only ever marks characters that are still resolving. Calm, typographic, slightly cryptic.
LOOK
- Colors: background #0A0A09, text #ECEBE4, muted #75746C, hairline rgba(236, 235, 228, 0.12), accent #C8F031.
- Font everywhere: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace. Because every glyph is the same width, the scramble never shifts the layout.
- Page: full viewport, padding clamp(20px, 4vw, 48px), a three-row grid (header, main, footer) with main vertically centered.
- Registration marks: a small "+" (12px, line-height 1, muted) in each of the four corners of the main area, 0 offset from its edges. Main has padding clamp(16px, 5vw, 64px) so the marks frame the content.
- Header row, space-between:
- Left: "decode/" at 14px, weight 600, #ECEBE4.
- Right: nav with four links, "Work", "Index", "Notes", "Contact". 13px, uppercase, letter-spacing 0.08em, muted, 28px gap (16px under 600px wide).
- Main, left aligned (justify-self start), width 100%, max-width 1120px:
- Label: "Transmission 04 / 07", 12px, uppercase, letter-spacing 0.14em, muted, preceded by an 8px accent square that blinks on a hard on/off cycle every 1.1s (visible for the first half, hidden for the second, no fade). 28px below it, the headline.
- Headline: two lines, each its own block span with white-space: pre and min-height 1.02em, so an empty line never collapses mid-decode. Size clamp(32px, 7.4vw, 104px), weight 600, line-height 1.02, letter-spacing -0.04em, #ECEBE4.
- Phrases, each exactly two lines:
1. "Ship the" / "strange idea."
2. "Make the" / "default rare."
3. "Every pixel," / "on purpose."
4. "Taste is a" / "feature."
- Under the headline, 28px below: "A tiny text effect for headlines and menus. Characters resolve left to right out of a cipher of glyphs, then hold." 15px, line-height 1.6, muted, max-width 46ch.
- Footer row: 1px hairline top border, 16px top padding, space-between, 12px uppercase, letter-spacing 0.12em, muted, numbers in #ECEBE4:
- Left: status. "Decoding 12/19" while resolving, "Locked 19/19" when done. Count only non-space characters of the target phrase (resolved / total).
- Right: "Phrase 2 / 4".
THE SCRAMBLE
- Glyph set: #%&*+=-_/\<>[]{}?!$@0123456789
- To transition from text A to text B (treat both lines as one sequence, line 1 then line 2, index i across both):
- start(i) = i × 28ms + random(0, 120ms)
- end(i) = start(i) + 180ms + random(0, 420ms)
- Before start(i): show A's character at i (or nothing if A is shorter on that line).
- Between start and end: show a random glyph from the set, re-rolled no more than once every 50ms for that position, wrapped in <span class="dud"> colored #C8F031 at 90% opacity.
- After end(i): show B's character.
- Spaces: if B has a space at i, it resolves to a space and never shows a glyph once resolved. If both A and B have a space at i, it stays a space the whole time.
- Render each frame with requestAnimationFrame by rebuilding each line from text nodes and .dud spans in a DocumentFragment. Never assign HTML strings.
- Headline cycle: resolve phrase 1 on load, hold 2600ms after it locks, scramble to the next phrase, and loop forever. Pause the cycle while document.hidden is true, and continue from the current phrase when visible again.
- Nav links: on mouseenter and focus, scramble the link from its text to the same text (so it decodes in place) with faster timing: start(i) = i × 22ms + random(0, 60ms), end(i) = start(i) + 120ms + random(0, 200ms). Ignore new triggers while that link is still scrambling. The link color goes from muted to #ECEBE4 on hover and focus.
STRUCTURE
- <header> with the wordmark and <nav aria-label="Primary">.
- <main> with the label, <h1>, and the paragraph.
- <footer> with the status and the phrase counter.
ACCESSIBILITY
- The h1 contains a visually hidden span with the first phrase as one sentence ("Ship the strange idea.") and the animated lines in an aria-hidden="true" wrapper, so screen readers never hear glyph noise.
- Each nav link has aria-label set to its word, and its animated text is aria-hidden="true".
- The footer status and counter are aria-hidden="true" (decorative).
- Links have a 2px #C8F031 focus ring with 4px offset.
- prefers-reduced-motion: no scramble, no cycling, no blinking. Show phrase 1, "Locked 19/19", and "Phrase 1 / 4". Hover only changes link color.
NEVER
- Matrix rain, green-on-black hacker styling, CRT scanlines, glitch offsets, or RGB split.
- A proportional font, centered text, a typewriter that types characters in one by one, or a blinking text cursor.
- More than one accent color, or the accent on anything except resolving characters and the label square.
- CopyThe full prompt, one click.
- PasteInto Cursor, Claude Code, or any agent.
- CompareAgainst the live demo above.
What’s Inside
Per-Character Timeline
Every character gets its own start and end time. A small stagger plus random jitter makes the wave read left to right without looking mechanical.
Throttled Glyphs
Resolving characters re-roll at most every 50ms, so the cipher shimmers instead of strobing.
Monospace Lock
Every glyph is the same width, so nothing in the layout moves while the text decodes.
Stable Lines
Each headline line is its own block with a minimum height, so an empty line never collapses mid-decode.
Space-Aware
Spaces in the target resolve to spaces, so word shapes appear before the words do.
No HTML Strings
Frames are built from text nodes and spans in a DocumentFragment. Nothing is ever parsed as HTML.
Quiet for Screen Readers
The h1 holds a hidden plain sentence, and the animated layer is aria-hidden. Links keep a clean aria-label.
Pause and Fallback
The cycle waits while the tab is hidden, and reduced motion shows the first phrase with no scramble at all.
Usage Guidelines
Use This When
- A portfolio, studio, or developer brand wants a typographic signature
- Short headlines or menu labels, roughly 30 characters or fewer
- The design is monospace or can carry a monospace display line
Not Ideal When
- Body copy or anything people need to read quickly
- Text that must be translated into scripts without a monospace face
- Interfaces where motion would distract from a task, like checkout or forms