Scramble Text
Decryption-style text animation — characters churn through random glyphs and lock in from left to right, triggered by viewport or hover.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add scramble-text.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<ScrambleText/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/scramble-text.en.jsonOr, once registries are configured in components.json, install it as @webberui/scramble-text.
Usage
import { ScrambleText } from "@/components/ui/scramble-text";
<ScrambleText
text="WEBBERUI DECRYPTED"
className="font-mono text-4xl font-bold"
/>Hover-to-replay mode, and mixed Chinese/English is no problem:
<ScrambleText text="Hover me: decryption animation" trigger="hover" duration={0.04} />A custom scramble character set (for example a hacker-style 0 and 1):
<ScrambleText text="BINARY MODE" charset="01" className="font-mono" />Scrambled from the very first paint (SSR-safe), with screen readers announcing the real text when it changes:
<ScrambleText text={status} initialScrambled live="polite" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | — | The target text; Chinese and emoji are both fine |
duration | number | 0.05 | Interval at which each character locks in (seconds); smaller decrypts faster |
charset | string | alphanumerics + symbols | The character set used for the scramble churn |
trigger | "view" | "hover" | "view" | Play automatically on entering the viewport, or replay on mouse hover |
once | boolean | true | With trigger="view", plays only the first time the element enters the viewport |
initialScrambled | boolean | false | Show deterministic-hash glyphs instead of the answer on the first render (including SSR); server and browser output match |
live | "off" | "polite" | "assertive" | "off" | aria-live level of the screen-reader text; off adds no aria-live |
How it works
- At its core is a single
requestAnimationFrameloop: it computes how many characters have locked in from the elapsed time (one more character locks from the left everydurationseconds), and every 2 frames the unlocked positions draw a new random glyph fromcharset, so the per-frame flicker is not too harsh. The animation writes straight into the DOM text node and does not trigger a React re-render; the previous rAF is cancelled on unmount and on replay - Grapheme splitting uses
Intl.Segmenter, so Chinese characters, emoji, and combining characters each count as one unit. Locking in restores the original character directly, so Chinese target text works exactly the same, while the scramble churn always usescharset - Whitespace does not scramble, keeping line width and line breaking stable throughout the animation
- Character width jitter: scrambled glyphs have different widths, so the layout jumps sideways. Pairing with
font-mono(a monospaced font) is recommended, or at leasttabular-numsin mostly numeric scenarios. When the target text contains Chinese, the width difference between Chinese and Latin makes the jitter more obvious, and a monospaced font works best - Accessibility: the complete text lives in an
sr-onlyelement and the animation layer is markedaria-hidden, so the screen reader always reads a complete sentence instead of scrambled glyphs; when the user has "reduce motion" enabled at the system level, the final text is shown directly with no animation initialScrambled: by default the first render outputs the answer itself (hidden via opacity until it enters the viewport whentrigger="view"). When enabled, the first render outputs scrambled glyphs instead; each glyph comes from an FNV-1a hash of "grapheme content + position" mapped intocharsetrather thanMath.random, so server and browser produce the same string and there is no hydration mismatch. Since the first paint is already scrambled and does not leak the answer, view mode no longer hides it first. Withtrigger="hover"the text stays scrambled until the first mouse enterlive: the real text lives in thesr-onlylayer;live="polite"or"assertive"addsaria-liveto that layer, so whentextchanges the screen reader announces the complete new text once, while the scrambled layer staysaria-hiddenand is never read out- With
once={false}andtrigger="view", leaving and re-entering the viewport replays the animation; withtrigger="hover"the text is shown statically and decrypts again on every mouse enter
Rolling Number
Odometer-style number wheels — when value changes, each digit springs vertically to its new value; added digits fade in and removed digits collapse.
Text Morph
A smooth morphing transition between two strings — shared characters slide to their new positions while the rest fade out and in, with auto-rotation and controlled switching.