A playful text hover effect where the words briefly scramble before settling into the new label. It works great for buttons, links, cards, and small interactive labels where you want the text to feel more dynamic.
Right-click in Elementor, choose “Paste from another site,” and while the popup is open, press cmd/ctrl + v to insert the layout.
Place the scripts before the body tag of your project. If you have added them before for another setup, skip this step.
<!-- GSAP Core -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/gsap.min.js"></script>
<!-- GSAP ScrambleText Plugin -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/ScrambleTextPlugin.min.js"></script>Paste the script through Elementor → Custom Code and set it to load after the closing body tag.
document.addEventListener("DOMContentLoaded", () => {
if (document.body.classList.contains("elementor-editor-active")) return;
if (!window.gsap || !window.ScrambleTextPlugin) return;
gsap.registerPlugin(ScrambleTextPlugin);
const defaultAnim = {
duration: 0.8,
ease: "none",
reset: true,
touch: false,
chars: "upperAndLowerCase",
tweenLength: true,
revealDelay: 0,
speed: 1,
delimiter: "chars"
};
const isTouch = window.matchMedia("(hover: none), (pointer: coarse)").matches;
const getNumber = (v, fb) => { const n = parseFloat(v); return Number.isFinite(n) ? n : fb; };
const getBoolean = (v, fb = false) => {
if (v === undefined || v === "") return fb;
if (v === "true") return true;
if (v === "false") return false;
return fb;
};
const getDelimiter = value => {
const v = value || defaultAnim.delimiter;
if (v === "words") return " ";
if (v === "chars") return "";
return defaultAnim.delimiter === "words" ? " " : "";
};
document.querySelectorAll('[data-scramble-hover-element="text"]').forEach(textEl => {
const wrap = textEl.closest('[data-scramble-hover-element="wrap"]') || textEl;
const original = textEl.textContent;
if (!original.trim()) return;
const swap = textEl.dataset.scrambleHoverSwap || original;
const duration = getNumber (textEl.dataset.scrambleHoverDuration, defaultAnim.duration);
const ease = textEl.dataset.scrambleHoverEase || defaultAnim.ease;
const reset = getBoolean(textEl.dataset.scrambleHoverReset, defaultAnim.reset);
const allowTouch = getBoolean(textEl.dataset.scrambleHoverTouch, defaultAnim.touch);
const chars = textEl.dataset.scrambleHoverChar || defaultAnim.chars;
const tweenLength = getBoolean(textEl.dataset.scrambleHoverTweenLength, defaultAnim.tweenLength);
const revealDelay = getNumber (textEl.dataset.scrambleHoverRevealDelay, defaultAnim.revealDelay);
const speed = getNumber (textEl.dataset.scrambleHoverSpeed, defaultAnim.speed);
const delimiter = getDelimiter(textEl.dataset.scrambleHoverDelimiter);
let isSwapped = false;
const play = text => {
gsap.killTweensOf(textEl);
gsap.to(textEl, {
duration, ease, overwrite: true,
scrambleText: { text, chars, tweenLength, revealDelay, speed, delimiter }
});
};
const enter = () => play(swap);
const leave = () => { if (reset) play(original); };
if (isTouch) {
if (!allowTouch) return;
wrap.addEventListener("click", () => {
if (reset) {
isSwapped = !isSwapped;
play(isSwapped ? swap : original);
} else {
play(swap);
}
});
return;
}
wrap.addEventListener("mouseenter", enter);
wrap.addEventListener("mouseleave", leave);
});
});Some solutions only work on the live site. Always publish and test after each change, as results may not appear in the editor.
Add data-scramble-hover-element="text" to the text element you want to animate.
This is the only required attribute. By default, the text itself becomes the hover trigger, so when the user hovers over the text, the scramble animation runs.
If you want the animation to trigger when hovering over a parent wrapper instead of the text itself, add data-scramble-hover-element="wrap" to the wrapper div.
This is useful for buttons, cards, links, or larger clickable areas where the text sits inside another element.
By default, the text scrambles back into its original value. You can change the hover text by adding data-scramble-hover-swap="VALUE" to the text element. For example, you could change Read More to View Project when the user hovers.
It’s best to keep the new text close in length to the original text, otherwise the layout may shift or feel a bit awkward during the animation.
By default, the scramble animation runs for 0.8 seconds. You can adjust the speed by adding data-scramble-hover-duration="VALUE" to the text element. For example, use 0.4 for a faster animation, 0.8 for the default speed, or 1.2 for a slower effect.
By default, the scramble speed is 1. You can adjust the intensity by adding data-scramble-hover-speed="VALUE" to the text element. For example, use 0.6 for a calmer effect, 1 for the default feel, or 2 for a faster and more energetic scramble.
By default, reveal delay is set to 0 seconds, meaning the final text starts revealing immediately. You can delay the reveal by adding data-scramble-hover-reveal-delay="VALUE" to the text element. For example, use 0.2 if you want the scramble to run briefly before the final letters start appearing.
By default, the animation uses none, meaning it plays at a consistent speed. You can change this by adding data-scramble-hover-ease="VALUE" to the text element. You can use any valid GSAP ease, such as power1.out, power2.out, power3.inOut, or none.
By default, the script uses upperAndLowerCase characters while scrambling. You can change this by adding data-scramble-hover-char="VALUE" to the text element. For example, use 0123456789 if you want a number-style scramble effect.
Common values include upperCase, lowerCase, upperAndLowerCase, or your own custom characters.
By default, tweenLength is set to true, so the text length smoothly changes if the swap text has a different number of characters. You can disable this by adding data-scramble-hover-tween-length="false" to the text element.
For example, if the original text is 50 characters and the replacement text is 100 characters, the number of characters will gradually move from 50 to 100 during the animation instead of jumping immediately to 100.
By default, the delimiter is set to chars, meaning the text scrambles character by character. You can change this by adding data-scramble-hover-delimiter="VALUE" to the text element. Use chars for letter-by-letter scrambling, or words if you want the text to reveal word by word instead.
By default, reset is set to true, so the text scrambles back to the original text when the mouse leaves. You can disable this by adding data-scramble-hover-reset="false" to the text element. Use false if you want the changed text to stay after hover instead of returning to the original text.
By default, the effect is disabled on touch devices because there is no real hover state. You can enable it by adding data-scramble-hover-touch="true" to the text element.
On touch devices, the animation runs on click instead of hover. If reset is true, each click toggles between the original text and the swap text. If reset is false, clicking always animates to the swap text.
You can also adjust the default global values directly inside the script. These values apply to every scramble hover text unless you override them with attributes on the text element.
To change the default animation behavior across all scramble hover elements, edit the values inside the defaultAnim object in the code.
const defaultAnim = {
duration: 0.8,
ease: "none",
reset: true,
touch: false,
chars: "upperAndLowerCase",
tweenLength: true,
revealDelay: 0,
speed: 1,
delimiter: "chars"
};