Text scramble
Text shuffles through glyphs and settles back into its own words, character by character.
Documentation
-
Copy to Webflow
Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: Webflow removes any font your site has not installed. -
Add custom CSS
Custom CSS in WebflowThis CSS already ships inside the Webflow embed, so a pasted section needs nothing added. Paste it yourself, in the page or the site custom code, only if the effect runs on several pages. The tutorial explains where.
See the tutorial -
Add custom JS
Custom JavaScript in WebflowThis script already ships inside the Webflow embed, settings block included. You paste it yourself, before the closing body tag of the page or the site custom code, only when the effect has to run on several pages, and the tutorial says how to choose.
See the tutorial -
Use it on your own elements
How to add a custom attribute in WebflowA custom attribute is a pair, a name and a value, typed in the Settings panel of the element you selected, under Custom attributes. The name is what the script looks for, the value is what it reads. The tutorial shows the panel.
See the tutorialCustom attributesSelect the element on the canvas and open the Settings panel. Under Custom attributes, add a pair: the name on the left, the value on the right.Custom attribute What it does data-text-scramble="scroll" the text settles as it comes into view data-text-scramble="hover" it plays when the pointer enters it, when it takes keyboard focus, and once per tap data-text-scramble="scroll hover" both: it settles on scroll, then plays again on every hover An empty or unknown value falls back to scroll, and says so in the browser console with the element and the three accepted values. The trigger is written here and nowhere else: no setting and no page-wide block can turn a hover into a scroll.SettingsEvery setting is a custom attribute too, added the same way and on the same element. An attribute always wins over the defaults written in the code.Attribute Default What it is data-text-scramble-color auto Colour of the glyphs while they are scrambled. Options: any CSS colour, or auto to keep the text's own colour. data-text-scramble-chars symbols digits The glyphs the text shuffles through. Options: symbols, digits, letters, any of them combined with spaces (symbols digits), or any other string, taken as the glyphs themselves (01, /|_). data-text-scramble-duration 0.5 How long one character stays scrambled before it settles, in seconds. data-text-scramble-stagger 0.03 Delay between two characters starting, in seconds. data-text-scramble-spread 1.2 The longest the wave may take from the first character to the last, in seconds; a long text shrinks its stagger to fit. data-text-scramble-speed 30 How many times per second a scrambled glyph changes, 1 to 120. data-text-scramble-order start Which characters settle first. Options: start (reading order), end, center (from the middle out), edges (from both ends in), random. data-text-scramble-delay 0 Wait before the effect starts, in seconds. data-text-scramble-trigger 0.86 How far into the screen the text has to come before it plays on scroll, as a share of the viewport height. data-text-scramble-replay false Whether a scroll effect plays again every time the text comes back into view. Options: false (once), true (every entry).
Two files and their pictures (2), no build step.
It runs in the browser only. Load it with a script tag in plain HTML, in a
client-side script in Astro, never in the frontmatter. In React, call
TextScramble.mount(ref.current) in useEffect and
destroy() in its cleanup.
-
Tune it, either way
Per element with an attribute, or once for the whole site by editingDEFAULTSat the top of the file.Attribute Default What it is Lower Higher data-text-scramble-color auto Colour of the glyphs while they are scrambled. Options: any CSS colour, or auto to keep the text's own colour. data-text-scramble-chars symbols digits The glyphs the text shuffles through. Options: symbols, digits, letters, any of them combined with spaces (symbols digits), or any other string, taken as the glyphs themselves (01, /|_). data-text-scramble-duration 0.5 How long one character stays scrambled before it settles, in seconds. A flicker, the glyphs barely show Every character churns for a long time data-text-scramble-stagger 0.03 Delay between two characters starting, in seconds. The whole text scrambles at once A clean wave running across the text data-text-scramble-spread 1.2 The longest the wave may take from the first character to the last, in seconds; a long text shrinks its stagger to fit. Long paragraphs rush through Long paragraphs take their time data-text-scramble-speed 30 How many times per second a scrambled glyph changes, 1 to 120. Slow, legible glyph swaps A fast shimmer, closer to noise data-text-scramble-order start Which characters settle first. Options: start (reading order), end, center (from the middle out), edges (from both ends in), random. data-text-scramble-delay 0 Wait before the effect starts, in seconds. Starts the moment it is triggered Lets another element go first data-text-scramble-trigger 0.86 How far into the screen the text has to come before it plays on scroll, as a share of the viewport height. It waits until it has climbed higher up the screen It plays as soon as it enters at the bottom data-text-scramble-replay false Whether a scroll effect plays again every time the text comes back into view. Options: false (once), true (every entry). -
Drive it from JavaScript
When text arrives after the page has loaded, when something other than a scroll or a hover has to play it, or when the page reacts to a play.const element = document.querySelector('[data-text-scramble]'); element.addEventListener('textscramble:done', (e) => { console.log(e.detail); }); const scramble = TextScramble.mount(element); // the element auto() mounted, or a new one scramble.play(); // plays it now, whatever its trigger is scramble.modes; // the triggers read from the attribute: ["scroll"], ["hover"], or both // Text added after the page loaded: mount everything marked inside it. TextScramble.auto(document.body); // When the text leaves the page: TextScramble.destroy(element); // ends the play and hands the words back
-
Fit it to your page
- Mark each text element, not its section:
data-text-scramble="scroll"on a heading, a paragraph, a label, a link, a button. Put it on a container and everything inside scrambles as one text, in one wave. - Settings go on the same element, as more attributes:
data-text-scramble-chars="digits",data-text-scramble-delay="0.2". Three levels, least specific first:DEFAULTS, thenwindow.TextScrambleSettings = { duration: 0.8 }declared before the engine, then the element's attributes. Settings are read again at every start. - Inline markup survives:
<strong>,<em>, links and coloured spans inside the text keep their tags, and their glyphs take their font and colour. Whitespace is never scrambled. - Hover listens to
pointerenterandfocusinon the element: a mouse, a pen, a finger (one play per tap) and the keyboard all start it. A hover that arrives while the effect runs is ignored, so a jittery pointer never restarts it and it always ends on the original text. - Scroll plays once the element crosses
trigger. A block too close to the end of the page to reach that line plays once the page is scrolled as far as it goes, and on a page too short to scroll, at once. An element already past that line when the page loads plays at once. - From JavaScript:
TextScramble.mount(el, overrides)returns{ element, settings, modes, play(), destroy() };overrides.mode('hover') is used only when the element's own value names no trigger.TextScramble.play(el),TextScramble.destroy(el),TextScramble.auto(root),TextScramble.defaultsandTextScramble.setscomplete it. Each play emitstextscramble:startandtextscramble:done, both bubbling, with{ element, trigger, modes }asdetail;triggerisscroll,hover,focus,api, orsafetywhen the safety net showed the text. - **Under
prefers-reduced-motion: reduce** nothing is scrambled and nothing is ever hidden. The events still fire, back to back, when the element is reached or hovered. - The words of the markup are the preview's, and they ship as they are: pasted, the page reads the way the preview did. Put the user's own content in their place whenever they want, in the page's language.
- On Webflow, add the fonts first. Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: the paste names them, and the Designer drops a font the site does not have.
- Mark each text element, not its section:
-
Avoid the pitfalls
- The stylesheet matters. Without
text-scramble.css, text waiting for the scroll is not hidden (it shows, then plays), and the effect empties the text inline while it runs, then gives thestyleattribute back as it found it. - **Settings handed to
mount()after the page loaded are dropped**:auto()mounts on DOM ready, and a secondmount()returns the first instance. Usewindow.TextScrambleSettings, or the attributes. - Do not hide waiting text yourself with
visibilityoropacity. The script makes it transparent, which keeps the words for screen readers and text search, and the safety net can only show what it hid. - **Text painted with
-webkit-text-fill-colororbackground-clip: text** (gradient text) fights the mechanism, which empties glyphs the same way. - **An ancestor with
overflow: hiddenthat is not positioned does not clip the glyphs**: the layer is absolutely positioned, so an ellipsis or a mask lets the scrambled glyphs of the hidden part show for the length of the effect. Give the clipping ancestorposition: relative. - **
text-shadowon the text disappears while its characters are scrambled**, and the glyphs carry none. - In Webflow, the trigger is read and changed in the Settings panel. The paste carries both examples at once, a section named "Scramble text (on scroll)" in the Navigator and a row of links named "Scramble text (on hover)", and every marked element carries
data-text-scrambleas a custom attribute, so its value is on screen where a Designer user already works. Changing the trigger is changing that value, on the element, and nothing else: no class, no page setting and no code. Each of the two examples can be deleted once the attribute is on the user's own text; only the Embed has to stay on the page. - A pointer state never survives the paste (AB-41), and this component needs none: what answers the pointer is the effect itself, which lives in the engine that travels inside the Embed. Do not move it to a Webflow class variant, and do not count on one for a hover colour either.
- The stylesheet matters. Without
One click on Copy the AI prompt copies everything an assistant needs to build this effect: the complete code, the markup, the dependency, every setting and every pitfall.
What it does
Text scramble is a text animation for Webflow and vanilla JavaScript, with no library. Also called text scramble on scroll and hover or scramble text.
Text shuffles through glyphs and settles back into its own words, character by character. Symbols, digits, letters, any mix of them, or a string of your own. It plays as the text comes into view on scroll, or when the pointer enters it, and the choice is written on the element itself, as the value of its attribute: data-text-scramble="scroll", "hover", or "scroll hover". The words never move: the native text stays exactly where it is, its scrambled characters are emptied one range at a time, and the glyphs are drawn over them in a layer of no size, each one picked among glyphs about as wide as the letter it covers. So nothing reflows, in a proportional face as in a mono, on one line or ten, at any width; links, bold, italics and coloured spans come back untouched; and a screen reader reads the real text the whole time. Pattern type text scramble, decode effect, terminal flavour.
Updated
Questions
Paste the component, then give your text element the custom attribute data-text-scramble with the value scroll, hover or scroll hover in the Settings panel. The trigger is that value and nothing else: no class, no page setting and no code. An empty or unknown value falls back to scroll and logs a console warning.
Yes. This text scramble uses no library and no build step. It empties the native characters with the CSS Custom Highlight API and draws the glyphs over them in an absolutely positioned layer.
data-text-scramble-chars takes symbols, digits, letters, a mix such as symbols digits (the default), or any string of your own such as 01. data-text-scramble-duration is how long one character stays scrambled, 0.5 seconds by default, and data-text-scramble-speed is how many times per second a glyph changes, 30 by default, from 1 to 120.
No. The native text stays where it is and each glyph is drawn over it in a layer of no size, picked among glyphs about as wide as the letter it covers, so no line can wrap differently. Gradient text painted with background-clip: text or -webkit-text-fill-color is the exception: it fights the mechanism.
Yes. Hover listens to pointerenter and focusin, so a mouse, a pen, a finger (one play per tap) and keyboard focus all start it. A screen reader reads the real text the whole time, and under prefers-reduced-motion: reduce nothing is scrambled and nothing is hidden.