Text is the most common element on any website, and one of the most commonly animated. But “text animation” covers a wide spectrum: a subtle word-by-word fade is a completely different tool than a chaotic RGB glitch.

Choosing the right approach sets the tone for the entire page. Pick wrong and the animation works against the content instead of reinforcing it.

This guide covers four distinct approaches to animating text with GSAP, with working code for each and a decision framework at the end.

Split and stagger reveals

Split text animations break content into characters, words, or lines, then animate each piece with a staggered delay. GSAP’s SplitText plugin handles the splitting and wrapping automatically. The result is a cascading reveal that draws the eye across the text in a controlled sequence.

This is the most versatile text animation approach. It works for hero headlines, section titles, and body copy. Depending on configuration, the feel ranges from a gentle fade to dramatic rotations.

// Split into words and reveal on scroll
const text = document.querySelector('.reveal-text');

if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  const split = SplitText.create(text, { type: 'words' });

  gsap.from(split.words, {
    y: 30,
    opacity: 0,
    stagger: 0.05,
    duration: 0.6,
    ease: 'power2.out',
    scrollTrigger: {
      trigger: text,
      start: 'top 80%',
    }
  });
}

The stagger value controls the delay between each word. Lower values (0.02-0.05) create a fast wave. Higher values (0.1-0.2) create a deliberate, word-by-word read. You can also split by chars or lines depending on the density of the effect you want.

Works best for: Hero headlines, section titles, and any text that should feel intentional and polished. This is the safe, versatile choice for most projects.

For a production-ready split text animation with scroll triggers, direction options, and built-in cleanup, see the Split Text Reveal effect.

Scramble and decode

Scramble effects replace characters with random glyphs, then resolve them one by one to reveal the final text. The effect feels like a cipher decrypting or a terminal booting up.

const el = document.querySelector('.decode-text');
const original = el.textContent;
const glyphs = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%';

if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  const obj = { progress: 0 };

  gsap.to(obj, {
    progress: 1,
    duration: 1.5,
    ease: 'none',
    onUpdate() {
      el.textContent = original
        .split('')
        .map((char, i) => {
          if (char === ' ') return ' ';
          if (i / original.length < obj.progress) return char;
          return glyphs[Math.floor(Math.random() * glyphs.length)];
        })
        .join('');
    }
  });
}

The character set matters more than you might expect. Alphanumeric characters feel techy. Katakana or symbols lean cyberpunk. Matching the glyph set to your design language sells the effect.

You can also control the decode direction. Resolving left-to-right feels like reading. Center-out feels like a burst. Random resolution feels chaotic. Each creates a noticeably different mood from the same underlying technique.

Works best for: Tech-themed landing pages, cyberpunk aesthetics, loading screens, and any context where text should feel computed or decrypted.

The Text Decode effect takes this concept further with five decode directions, custom character sets, and multiple trigger modes.

Glitch and distortion

Glitch effects layer copies of the text with offset positions and color channel separation. The result looks like a digital signal breaking down: horizontal shifts, RGB splits, and scan line artifacts.

The key technique is layering elements with blend modes:

<div class="glitch-text" data-text="SIGNAL LOST">
  <span class="glitch-layer red">SIGNAL LOST</span>
  <span class="glitch-layer cyan">SIGNAL LOST</span>
  SIGNAL LOST
</div>
.glitch-text {
  position: relative;
  font-weight: bold;
}

.glitch-layer {
  position: absolute;
  inset: 0;
  mix-blend-mode: multiply;
}

.glitch-layer.red { color: #ff0040; }
.glitch-layer.cyan { color: #00f0ff; }
// Random glitch bursts
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  function glitchBurst() {
    const tl = gsap.timeline();

    tl.to('.glitch-layer.red', {
      x: gsap.utils.random(-4, 4),
      skewX: gsap.utils.random(-2, 2),
      duration: 0.08,
    })
    .to('.glitch-layer.cyan', {
      x: gsap.utils.random(-4, 4),
      skewX: gsap.utils.random(-2, 2),
      duration: 0.08,
    }, '<')
    .to('.glitch-layer', {
      x: 0,
      skewX: 0,
      duration: 0.06,
    });

    // Next burst at a random interval
    gsap.delayedCall(gsap.utils.random(2, 5), glitchBurst);
  }

  glitchBurst();
}

The randomness is what sells glitch effects. Fixed, looping patterns look mechanical. Randomized intervals and offsets feel like actual signal interference.

Use glitch sparingly. A single glitching headline grabs attention. An entire page of glitching text is unreadable.

Works best for: Gaming sites, cyberpunk themes, error pages with personality, and any brand that leans edgy or experimental. Not suited for body copy or long text.

The Glitch Text effect provides configurable RGB separation, scan lines, and randomized burst timing out of the box.

Cursor-driven reactions

Cursor-driven text animation makes individual characters respond to mouse proximity. Characters push away, pull toward, or rotate based on their distance from the pointer. The text feels alive.

const text = document.querySelector('.reactive-text');
const original = text.textContent;

// Wrap each character in a span
text.textContent = '';
for (const char of original) {
  const span = document.createElement('span');
  span.className = 'char';
  span.style.display = 'inline-block';
  span.textContent = char === ' ' ? '\u00A0' : char;
  text.appendChild(span);
}

if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  const spans = text.querySelectorAll('.char');

  document.addEventListener('mousemove', (e) => {
    spans.forEach(span => {
      const rect = span.getBoundingClientRect();
      const cx = rect.left + rect.width / 2;
      const cy = rect.top + rect.height / 2;
      const dist = Math.hypot(e.clientX - cx, e.clientY - cy);
      const radius = 100;

      if (dist < radius) {
        const force = (1 - dist / radius) * 20;
        const angle = Math.atan2(cy - e.clientY, cx - e.clientX);
        gsap.to(span, {
          x: Math.cos(angle) * force,
          y: Math.sin(angle) * force,
          duration: 0.3,
        });
      } else {
        gsap.to(span, {
          x: 0,
          y: 0,
          duration: 0.6,
          ease: 'elastic.out(1, 0.3)',
        });
      }
    });
  });
}

This approach runs calculations on every mousemove event for every character. For short headlines, that’s fine. For longer text, use gsap.quickTo() instead of creating new tweens on each frame, and consider limiting the effect to characters within a bounding box near the cursor.

This is inherently a desktop effect. On touch devices, there’s no persistent cursor position, so you’ll need a fallback or simply skip the effect.

Works best for: Interactive hero headings, creative navigation menus, and portfolio sites where the audience expects (and uses) a mouse. Keep it to short text, not paragraphs.

The Text Hover Distortion effect adds four distortion modes (push, pull, wave, rotation), configurable influence radius, and performance-optimized tracking.

Choosing the right approach

The right text animation depends on the project’s tone and the content’s purpose.

Use split reveals when:

  • The site tone is professional, editorial, or minimal
  • The text is a heading or title that needs polish
  • You want a safe, versatile option that works on any project
  • The animation should enhance readability, not compete with it

Use scramble/decode when:

  • The design has a tech, sci-fi, or cyberpunk theme
  • Text should feel computed, revealed, or decrypted
  • You’re building a loading state or transition screen
  • The effect is triggered by scroll or page load

Use glitch effects when:

  • The brand is edgy, experimental, or gaming-related
  • You want attention on a single headline, not body copy
  • Brief, intermittent bursts (not continuous distortion)
  • You’re comfortable with a polarizing aesthetic

Use cursor reactions when:

  • The site targets desktop users
  • Interactivity is a core part of the brand experience
  • The text is short: a heading, a name, a nav label
  • You want the page to feel responsive and alive

Combining approaches: These categories aren’t mutually exclusive. A split reveal on scroll that transitions to a cursor-reactive state on hover is a natural combination. Scramble decode for the initial reveal, then glitch bursts on a timer, works for cyberpunk themes. Layer intentionally, not just because you can.

Accessibility

All four approaches share the same accessibility baseline. The prefers-reduced-motion media query lets you detect when a user has requested less motion at the OS level.

const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (prefersReducedMotion) {
  // Show text in its final state, skip animation
  return;
}

Beyond the motion check:

  • Split reveals: The final text is the original content. Screen readers see it normally.
  • Scramble/decode: Set aria-label on the element with the final text so screen readers don’t announce random characters mid-animation.
  • Glitch effects: Avoid rapid flashing. Keep glitch bursts to subtle shifts, not strobe-like flickers.
  • Cursor reactions: Characters return to their original position when the cursor leaves. The text remains readable at all times.

The goal is that users who opt out of motion still see the content clearly, and users who keep motion enabled never encounter flashing or strobing patterns.

Performance

Split reveals create one DOM element per character, word, or line. For a short headline, that’s a handful of elements. For a paragraph split by characters, that could be hundreds. Stick to word or line splitting for longer text.

Scramble effects are lightweight. The DOM stays as a single text node, and the work happens in string manipulation on each frame.

Glitch effects use mix-blend-mode, which triggers compositing. On most hardware this runs smoothly, but stacking multiple glitched elements can add up. Test on lower-end devices if you’re using the effect in more than one place.

Cursor reactions run per-character distance calculations on every mouse event. Use gsap.quickTo() for the displacement tweens, and avoid applying the effect to more than 30-40 characters at once.

Wrapping up

Each of these four approaches solves a different problem. Split reveals are the workhorse for clean, professional text animation. Scramble decode adds thematic texture. Glitch effects make a bold statement. Cursor reactions create interactivity.

Pick based on the project’s tone and the content’s purpose, not on which effect looks most impressive in isolation. The best text animation is the one that reinforces the message without distracting from it.


Looking for production-ready text animations with full configuration, accessibility, and cleanup built in? Browse the GSAP Vault effects library for text effects, scroll animations, and interactive components you can drop into any project.