Cursors✨ Premium

Cursor Text Scramble

Every character becomes a span; on mousemove, those whose center is within 60 px of the pointer receive a random character out of 69 and the color #a78bfa, then come back one by one 200 to 500 ms later — no CSS animation, only setTimeouts. The sold HTML delivers the sentence as plain text: the script produces 44 spans, one per character.

JSTextInteractive

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. Cursors has 26 effects, including 1 free. Explore the category →

3 usage examples

Studio signature — hero in capitals — Cursor Text Scramble example 1

① Studio signature — hero in capitals

WhenHome page of a design studio: one line of spaced capitals as a signature, a tagline below.
WhyA short signature in capitals is the ideal target: few letters, so a cheap measurement per event, and scrambling three or four letters around the pointer is enough to say “interactive” without ever making the line unreadable. The tagline, outside .scramble-text, stays stable.
SettingsMonospaced font and white-space: nowrap — a drawn glyph keeps the original's width, the line never jumps —, 20 px, letter-spacing: 2px, radius lowered to 45 px to touch only two or three letters.
Event poster — glitched title — Cursor Text Scramble example 2

② Event poster — glitched title

WhenVisual of a tech or electro event: title, subtitle, date and venue on a dark background.
WhyASCII-character scrambling is the “glitch” aesthetic of live-coding and synthwave posters; here it isn't gratuitous, it is driven by the hand. The 69-sign set (digits and symbols included) gives the expected grain.
Settings30 px text on two lines, radius 80 px, cyan scramble color #22d3ee, character set reduced to 0123456789!@#$%&* for a more “code” feel.
Save the date — invitation card — Cursor Text Scramble example 3

③ Save the date — invitation card

WhenInvitation card or screen: a “Save the date” line, then date, time and venue in large characters.
WhyA date that scrambles and recomposes under the mouse draws the eye to the information to remember, without a looping animation. Digits and separators become readable again as soon as the pointer moves away (200 to 500 ms).
SettingsMonospaced font and white-space: nowrap for a date that never rewraps onto two lines, 20 px, radius 60 px kept, pink scramble color #f472b6, user-select restored so the date can be copied.

How it works

On load, for each [data-text-scramble], the script takes the first .scramble-text, reads its textContent, empties it and recreates one span.char per character, with the original stored in data-original. The letters' CSS: display: inline-block (so each has a measurable box), transition: color .1s, and .char.scrambling { color: #a78bfa }. The text itself is 1.3 rem bold, letter-spacing: 2px, #e2e8f0, user-select: none, centered in a flex zone with padding: 30px.

On every mousemove over the zone, the script walks all the letters, measures each one (getBoundingClientRect) and computes the distance from its center to the pointer. If it is under 60 px and the original isn't a space, the letter gets the scrambling class and a character drawn from a 69-sign string (A–Z, a–z, 0–9, !@#$%&*). Its return timer is cleared then re-armed: setTimeout(…, 200 + 300 × Math.random()) — the letter recovers data-original and loses the class 200 to 500 ms after the pointer's last pass. As long as the mouse moves within range, each event draws a new character: that is the flicker.

Measured in Chromium: after a sweep across the text, the last character returns 497 ms after the last movement and the sentence is restored identically. There is no requestAnimationFrame and no keyframe: the only animation is the 100 ms color transition. The cost is the measuring: one geometry read per letter per event — negligible for 44 letters, worth watching on a paragraph. The native pointer stays displayed (cursor: default).

The sold HTML delivers the sentence as plain text inside .scramble-text: textContent holds exactly the 44 characters and the script creates 44 spans, one per letter and per space. That is the working condition, since the script re-reads textContent and re-splits everything, and the condition only spares the space " ". Indented HTML — the sentence pre-split into 44 span.char elements, each on its own line, as in an older extraction — made it count 45 line breaks and 272 indentation spaces, i.e. 357 spans instead of 44 (317 of zero width); within reach of the pointer, the line breaks received a visible character — measured on that HTML: 7 scrambled at once, stray letters between the words (“C U R S O kI D vL hM…”) and a line widening from 637 to 684 px.

Accessibility

  • prefers-reduced-motion not handled: the letter flicker isn't a CSS animation, no media query stops it. Gate the mousemove listener on !matchMedia('(prefers-reduced-motion: reduce)').matches — the text then stays a text, readable and stable, which is the expected fallback.
  • The effect is mouse-only; on touch, at most one event per tap, with no consequence. The text carries no extra information while scrambled — it loses some, temporarily: don't apply it to an instruction that must be read at the very moment the mouse passes (menu, button label). user-select: none also prevents selecting and copying the sentence: remove it if this is content.
  • Contrast: #e2e8f0 on #0a0a0f = 16:1 and the scramble color #a78bfa = 7.3:1, AAA. On a light page those values collapse (1.2:1 and 2.7:1 on white): switch the text to #1e293b (14.6:1) and the scramble color to #6d28d9 (7.1:1).
  • Screen readers: the sentence is split into dozens of inline-block spans, and its content changes dozens of times per second on hover. Some readers spell inline-blocks one by one; all will read random characters if the user moves the mouse at the same time. Give .scramble-text an aria-label carrying the sentence (or a visually hidden copy) and aria-hidden="true" on the spans, so the spoken version is always the real one.
  • Integration: the HTML must provide the sentence as plain text inside .scramble-text, as the sold code does — pre-split, indented spans would produce stray whitespace spans. The split happens once on load: text changed afterwards (translation, CMS) isn't picked up, re-run the script after. The zone needs a height — height: 100% inside the sold container yields 93 px, the text band plus 30 px of padding — and the pointer must pass within 60 px of the letters' centers.

Browser compatibility

ES5 code (var, function): it requires getBoundingClientRect, classList, setTimeout and data-*. No CSS animation apart from a color transition. Zero dependencies.

Chrome 8+✓ Full
Firefox 3.6+✓ Full
Safari 5.1+✓ Full
Edge 12+✓ Full
Mobile iOS✓ Static (no mousemove)
Android Chrome✓ Static (no mousemove)

Without JavaScript, the sentence displays as is, as plain text or in its spans: nothing missing, nothing moving. Without CSS transitions, the violet color appears and disappears abruptly.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="scramble-zone" data-text-scramble>
  <div class="scramble-text">CURSOR ALCHEMY TRANSFORMS DIGITAL EXPERIENCE</div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Action radius (JS — < 60) 60 px Distance from the pointer to a letter's center below which it scrambles. 30 touches one or two letters; 120 scrambles a whole word. At large font sizes, scale it up accordingly.
Character set (JS — 69-sign string) A–Z a–z 0–9 !@#$%&* Characters drawn at random. Careful: the index is Math.floor(69 × Math.random()) — if you change the string, put its length in place of 69, otherwise out-of-range indexes return undefined, displayed as is.
Return delay (JS — 200 + 300 × Math.random()) 200 to 500 ms Time between the pointer's last pass and the letter's return. The timer is reset on every event: the letter only returns once the pointer has left. 80 + 120 × … = quick return; 600 + 900 × … = long trail.
Scramble color (CSS — .char.scrambling) #a78bfa Color of the letters while scrambled. The switch runs through transition: color .1s. Check 4.5:1 on your background (7.3:1 here on #0a0a0f).
Spaces (JS — " " !== data-original) only spaces are spared Replace the comparison with !/\s/.test(original) to also spare line breaks and tabs — essential if your HTML is indented or spans several lines. Spare punctuation too by adding it to the test.
Typography (CSS — .scramble-text) 1.3 rem, 700, 2 px letter-spacing The larger the font, the fewer letters fit within the 60 px radius: adjust the radius with the size. A monospaced font prevents the line from shifting when a narrow letter (i, l) becomes a wide one (W, @).
Target (HTML — .scramble-text, plain text) first .scramble-text of the zone The script only processes the first .scramble-text of each [data-text-scramble] zone. Provide the sentence as plain text, without spans or indentation; for several titles, several zones.

FAQ

Because the script re-reads the textContent of .scramble-text and creates one span per character, line breaks and indentation included — and the condition only spares the space " ". The sold code delivers the sentence as plain text on a single line (<div class="scramble-text">CURSOR ALCHEMY …</div>, 44 spans). If you rewrote it over several indented lines, or if a tool froze the DOM into 44 neatly formatted span.char elements, every line break "\n" becomes a span that scrambles like a letter and shows a visible character where there was nothing — measured on such HTML: 357 spans instead of 44, signs between the words and a line going from 637 to 684 px. Two remedies, your pick: keep the sentence as plain text on one line, or replace the comparison with !/\s/.test(original).
Each mousemove draws a new character for every letter within reach and resets its return timer (clearTimeout then setTimeout). As long as the pointer stays within 60 px, the letter flickers and its return is pushed back; as soon as it moves away, the last timer set runs out — 200 to 500 ms, drawn at random per letter, hence the scattered return. Measured: 497 ms between the last movement and the last restored letter. For a synchronized return, replace the random term with a constant.
Several titles: one [data-text-scramble] zone per title, each with its own .scramble-text — the script only looks at the first one in each zone. Translation or content loaded afterwards: the split into spans happens once on load, on the text present at that moment; if you replace the text later (client-side i18n, CMS), the spans the script remembers no longer exist and hovering does nothing. Re-run the initialization function after writing the text, or render the final sentence server-side.