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 six distinct approaches to animating text with GSAP, with working code for each and a decision framework at the end. If you would rather browse finished implementations than read the theory, the GSAP text effects category has a live demo and full source for every one of them.

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.

Live, interactive preview. Get the full code →

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.

Typewriter and terminal

A typewriter effect reveals text one character at a time at a steady rate, as though it were being typed. It is the oldest text animation on the web and still the clearest way to signal that something is being composed rather than merely displayed: a terminal prompt, a chat response, a tagline that cycles through several phrases.

Live, interactive preview. Get the full code →

The naive implementation uses setInterval and drifts out of sync with everything else on the page. Driving it from a GSAP tween instead keeps it on the same clock as the rest of your animation, so it can be sequenced in a timeline, paused, reversed or scrubbed like anything else.

const el = document.querySelector('.typed');
const full = el.textContent;
const state = { chars: 0 };

el.textContent = '';

gsap.to(state, {
  chars: full.length,
  duration: full.length * 0.045,
  ease: 'none',
  onUpdate: () => {
    el.textContent = full.slice(0, Math.round(state.chars));
  },
});

Two details separate a good typewriter from an irritating one. Reserve the final height in CSS so the block does not grow line by line and shove the page around as it types, and keep the full string in the DOM for assistive technology rather than building it up character by character. A min-height on the container and an aria-label carrying the finished text cover both.

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.

Live, interactive preview. Get the full code →
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 GSAP scramble text effect in the vault 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.

Live, interactive preview. Get the full code →

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 GSAP glitch effect in the vault provides configurable RGB separation, scan lines, and randomized burst timing out of the box.

Scroll-linked highlight

Everything above is triggered by scroll, hover or load and then plays on its own clock. A scroll-linked highlight is different: the animation is scrubbed, so its progress is tied directly to scroll position. Text dims to a muted tone and fills back to full contrast word by word as the reader moves down the page, which turns a paragraph into a reading pace rather than a reveal.

Live, interactive preview. Get the full code →

The mechanic is a stagger inside a scrubbed ScrollTrigger. Because scrub ties timeline progress to scroll progress, the reader controls the animation in both directions, and scrolling back up unwinds it exactly.

const split = SplitText.create('.lede', { type: 'words' });

gsap.fromTo(split.words,
  { opacity: 0.25 },
  {
    opacity: 1,
    stagger: 0.1,
    ease: 'none',
    scrollTrigger: {
      trigger: '.lede',
      start: 'top 75%',
      end: 'bottom 55%',
      scrub: true,
    },
  }
);

The trap is contrast. Muted text is still text that someone has to read, so the dimmed state needs to stay legible on its own rather than relying on the animation to rescue it. Starting from 0.25 opacity on a dark background is usually the floor, and a reduced-motion fallback should render every word at full contrast immediately.

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.

Live, interactive preview. Move your cursor across the text inside the frame. Get the full code →
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 a typewriter when:

  • The text should read as being composed in real time
  • You are building a terminal, chat or command-line aesthetic
  • A single line needs to cycle through several phrases
  • The container can reserve its final height so nothing shifts

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 scroll-linked highlight when:

  • The text is a lede or pull quote you want read, not skimmed
  • The page already has a deliberate scroll pace to tie into
  • You want the reader in control of the animation in both directions
  • The dimmed state can stay legible on its own

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 six 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.
  • Typewriter: Keep the finished string in an aria-label so assistive technology reads the whole line rather than a growing fragment.
  • 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.
  • Scroll-linked highlight: The dimmed start state must pass contrast on its own, since a reader who never scrolls past it still has to read it.
  • 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.

Typewriter effects rewrite textContent on every frame, which is cheap for a line and wasteful for a paragraph. Scroll-linked highlight is the heaviest of the six on long copy, because a scrubbed stagger keeps every split word under tween control for the whole scroll range: split by word rather than character, and keep the scrubbed range to a single paragraph.

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

Before adding movement, check that the static page makes its reading order clear. The free visual hierarchy course for developers lets you practise type size, spacing and emphasis on a live page. For timing, easing and reduced-motion decisions, the motion design course has a practical curriculum included with individual Vault access.

Each of these six approaches solves a different problem. Split reveals are the workhorse for clean, professional text animation. Typewriter signals composition in progress. Scramble decode adds thematic texture. Glitch effects make a bold statement. Scroll-linked highlight sets a reading pace. 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? Every effect above lives in the GSAP text effects category with a live demo and the full source, and the GSAP effects guide covers which plugin to reach for and how to wire one into your own page.