Text Scramble / Glitch Effect

Typewriter-style text reveal with random character scrambling before settling on final text. Customizable scramble duration, character sets (alphanumeric, symbols, binary, custom), and triggers: on load, on hover, on scroll.


Text Scramble / Glitch Effect


A modern text effect that reveals copy by scrambling random characters before each one locks into place—typewriter meets glitch. Fully configurable: scramble duration, character sets, and when it runs (on load, on hover, or when the element scrolls into view).

Features

HTML

Use a single element (e.g. span or h1) and data attributes. The element’s initial text is the final revealed text.

<!-- Runs once when the page loads -->
<h1 class="scramble-text" data-scramble data-scramble-trigger="load">
  Hello, World.
</h1>

<!-- Runs when the user hovers (resets on mouse leave) -->
<p class="scramble-text" data-scramble data-scramble-trigger="hover">
  Hover to unscramble.
</p>

<!-- Runs when the element scrolls into view -->
<h2 class="scramble-text" data-scramble data-scramble-trigger="scroll">
  Scroll to reveal.
</h2>

Options (data attributes)

AttributeValuesDescription
data-scramble-triggerload | hover | scrollWhen to run the effect.
data-scramble-durationnumber (ms)Total time for full reveal. Default e.g. 2000.
data-scramble-speednumber (ms)Interval for redrawing scrambling chars. Default e.g. 40.
data-scramble-charsetalphanumeric | symbols | binary | customWhich characters to use while scrambling.
data-scramble-customstringCustom charset when data-scramble-charset="custom".

JavaScript (core idea)

Store the final string, then in a loop: for each index from 0 to length, show final text for indices < current, one random character at current, and random (or same) for the rest. Advance the current index on a timer; when current reaches length, clear the interval and set the text to the final string.

const CHARSETS = {
  alphanumeric: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
  symbols: "!@#$%^&*()_+-=[]{}|;:,.<>?",
  binary: "01",
};

function scrambleText(el, options = {}) {
  const final = (el.textContent || "").trim();
  const duration = options.duration ?? 2000;
  const speed = options.speed ?? 40;
  const charset = options.charset ?? CHARSETS.alphanumeric;
  const chars = charset.split("");

  let index = 0;
  const len = final.length;
  const step = Math.max(1, Math.ceil((len * speed) / duration));

  const tick = () => {
    let out = final.slice(0, index);
    for (let i = index; i < len; i++) {
      out += chars[Math.floor(Math.random() * chars.length)];
    }
    el.textContent = out;
    index += step;
    if (index < len) requestAnimationFrame(tick);
    else el.textContent = final;
  };
  requestAnimationFrame(tick);
}

Wire triggers: on load call scrambleText(el) once; on hover call it on mouseenter (and optionally reset text on mouseleave and run again next time); on scroll use IntersectionObserver and call scrambleText(entry.target) when isIntersecting.

CSS: optional glitch during scramble

Add a class while scrambling and remove it when done. Use a fast text-shadow or filter animation for a subtle glitch.

.scramble-text.scrambling {
  animation: glitch-flicker 0.08s steps(1) infinite;
}

@keyframes glitch-flicker {
  0%, 100% { filter: none; }
  50% { filter: contrast(1.1); opacity: 0.98; }
}

Resources