Text Scramble (Load, Scroll, Hover)
Three scramble text patterns in one script: characters randomise on page load, on scroll entry, and on hover. Uses GSAP's ScrambleTextPlugin with SplitText for a per-word scramble effect, with configurable character sets.
GSAPScrambleTextSplitTextScrollTriggerHover
Setup — External Scripts
GSAP CDN
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>ScrambleTextPlugin CDN
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/ScrambleTextPlugin.min.js"></script>SplitText CDN
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/SplitText.min.js"></script>ScrollTrigger CDN
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/ScrollTrigger.min.js"></script>Code
index.html
html
<div class="demo-group">
<!-- Scramble on load -->
<div class="scramble-section">
<h1 data-scramble="load" class="scramble-heading">This heading will reveal with a basic scrambling effect on page load</h1>
</div>
<!-- Scramble on scroll -->
<div class="scramble-section u--bg-light">
<h2 data-scramble="scroll" class="scramble-heading">This heading is triggered by a ScrollTrigger</h2>
</div>
<!-- Scramble on scroll — alternate character set -->
<div class="scramble-section">
<h2 data-scramble-alt="" data-scramble="scroll" class="scramble-heading">You can control the characters used during scramble</h2>
</div>
<!-- Scramble on hover -->
<div class="scramble-section u--bg-light">
<h2 class="scramble-heading">And here's how to work with scramble text on hover:</h2>
<a data-scramble-hover="link" href="#" class="scramble-button">
<p data-scramble-text="this text can be custom too" data-scramble-hover="target" class="scramble-button-text">How to scramble on hover</p>
</a>
</div>
</div>styles.css
css
.scramble-section {
grid-column-gap: 2em;
grid-row-gap: 2em;
flex-flow: column;
justify-content: center;
align-items: center;
width: 100%;
min-height: 100vh;
display: flex;
}
.scramble-section.u--bg-light {
background-color: #efeeec;
}
.scramble-heading {
text-align: center;
letter-spacing: -.03em;
text-transform: uppercase;
max-width: 12em;
margin: 0 auto;
font-family: RM Mono, Arial, sans-serif;
font-size: 3em;
font-weight: 400;
line-height: .9;
}
.scramble-button {
color: #131313;
text-transform: uppercase;
border: 1px dotted #000;
border-radius: .3125em;
padding: .5em 1em;
font-family: RM Mono, Arial, sans-serif;
font-size: 1em;
font-weight: 400;
text-decoration: none;
}
.scramble-button-text {
margin: 0;
}script.js
javascript
gsap.registerPlugin(ScrollTrigger, ScrambleTextPlugin, SplitText)
function initScrambleOnLoad() {
document.querySelectorAll('[data-scramble="load"]').forEach(target => {
const split = new SplitText(target, { type: "words, chars", wordsClass: "word", charsClass: "char" });
gsap.to(split.words, {
duration: 1.2,
stagger: 0.01,
scrambleText: { text: "{original}", chars: 'upperCase', speed: 0.85 },
onComplete: () => split.revert(),
});
});
}
function initScrambleOnScroll() {
document.querySelectorAll('[data-scramble="scroll"]').forEach(target => {
const isAlternative = target.hasAttribute("data-scramble-alt");
const split = new SplitText(target, { type: "words, chars", wordsClass: "word", charsClass: "char" });
gsap.to(split.words, {
duration: 1.4,
stagger: 0.015,
scrambleText: {
text: "{original}",
chars: isAlternative ? '▯|' : 'upperCase',
speed: 0.95,
},
scrollTrigger: { trigger: target, start: "top bottom", once: true },
onComplete: () => split.revert(),
});
});
}
function initScrambleOnHover() {
document.querySelectorAll('[data-scramble-hover="link"]').forEach(target => {
const textEl = target.querySelector('[data-scramble-hover="target"]');
const originalText = textEl.textContent;
const customHoverText = textEl.getAttribute("data-scramble-text");
new SplitText(textEl, { type: "words, chars", wordsClass: "word", charsClass: "char" });
target.addEventListener("mouseenter", () => {
gsap.to(textEl, {
duration: 1,
scrambleText: { text: customHoverText || originalText, chars: "◊▯∆|" },
});
});
target.addEventListener("mouseleave", () => {
gsap.to(textEl, {
duration: 0.6,
scrambleText: { text: originalText, speed: 2, chars: "◊▯∆" },
});
});
});
}
document.addEventListener("DOMContentLoaded", () => {
initScrambleOnLoad();
initScrambleOnScroll();
initScrambleOnHover();
});Notes
- •Splitting text into individual words before applying `scrambleText` gives a nicer per-word scramble cadence than applying it directly to the whole element — each word resolves independently.
- •`split.revert()` is called in `onComplete` to remove all the extra character spans from the DOM after animation, keeping the page lean.
- •The `chars` option in `ScrambleTextPlugin` accepts `'upperCase'`, `'lowerCase'`, `'digits'`, or any custom string of characters (e.g. `'▯|'`, `'◊▯∆'`).
- •Hover scramble reads `data-scramble-text` for a custom target string — if omitted, it rescrambles back to the original text on both enter and leave.
- •The scroll version uses `once: true` so each heading only scrambles the first time it enters the viewport.