Scramble Text
A 30 ms setInterval replaces each character with a random symbol from a 42-glyph set, then reveals them left to right at one-third of a position per tick — a decoder effect triggered on hover.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Text has 56 effects, including 4 free. Explore the category →
3 usage examples



How it works
On mouseenter, a setInterval fires every 30 ms. Each tick rebuilds the full text content: positions below iterations return the original character, positions above draw randomly from 42 symbols (A–Z, 0–9, @#$%&*). The result is assigned in one shot via textContent — no per-character DOM manipulation.
iterations advances by 1/3 per tick: each position stays scrambled for roughly three ticks (≈ 90 ms) before locking onto the correct character. Revelation moves strictly left to right — the eye reads a progressive decoder rather than random noise.
The interval stops as soon as iterations >= originalText.length (all characters revealed). On mouseleave, clearInterval cancels the current interval and textContent is restored instantly to its original value — no exit animation.
The target element uses "Courier New", monospace: fixed glyph width regardless of symbol, zero layout shift during replacement. The indigo color #6366f1 reinforces the terminal/system aesthetic.
Accessibility
- prefers-reduced-motion: absent from the code — the effect fires even if the OS requests reduced motion. Add
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;at the top of themouseenterhandler to disable the scramble. - No keyboard trigger: the effect only listens on
mouseenter/mouseleave— keyboard users never see the animation. Recommendation: mirror the same handlers onfocus/blur. - Repeated
textContentupdates may be announced by some screen readers on every tick. Recommendation: addaria-hidden="true"to the scrambled element and include a<span class="sr-only">with the static text outside the flow. - The original text is always readable before hover and is restored instantly on
mouseleave— no locked state. - The monospace font keeps the line width stable during scramble: no layout reflow, no shift of adjacent content.
Browser compatibility
Requires setInterval and textContent — supported in every browser since 2010. Zero external dependencies.
Without JS, the text stays static, styled in indigo monospace — consistent render, no fatal error.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="demo-preview">
<span class="demo-text splitting-text-scramble" id="scrambleTextHover">YOUR TEXT</span>
<!-- Optional: Replay button -->
<button class="replay-btn" onclick="replayScramble()">Replay</button>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| chars (JS, line 7) | 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%&*' | 42-symbol replacement set. Narrow to 'ABCDEF0123456789' for a hex look, or '!@#$%^&*' for a more aggressive tone. |
| setInterval (JS, line 14) | 30 | Delay in ms between ticks. 20 ms = ultra-fast decoder; 60 ms = slow, dramatic deciphering. |
| iterations += (JS, line 22) | 1/3 | Reveal step per tick. 1/3 → 3 ticks/char (~90 ms/char); 1/2 → ~60 ms/char (snappy); 1/6 → ~180 ms/char (slow). |
| color (.splitting-text-scramble) | #6366f1 | Text color for both the scrambled and final states. Swap in your brand color. |
| font-family (.splitting-text-scramble) | "Courier New", monospace | Monospace is required to prevent width jumps. Swap to 'JetBrains Mono', 'Fira Code', or any installed monospace face. |
| font-size (.demo-text) | 2.5rem | Text size. The larger the text, the more visible the scramble — scale to your typographic hierarchy. |
FAQ
getElementById('scrambleTextHover'). If your span has a different id (e.g. id="scrambleText"), either add id="scrambleTextHover" to your element or update the getElementById line to match your actual id.mouseenter listener with a direct call after initialization: remove the addEventListener('mouseenter', ...) block and place its body (the setInterval) directly inside the IIFE, after scrambleInterval is declared. The Replay button can then re-trigger it via scrambleEl.dispatchEvent(new Event('mouseenter')).getElementById with querySelectorAll('.splitting-text-scramble') and initialize each element in a loop — each needs its own scrambleInterval and its own originalText copy.