Capability 04 — Split text

Four ways to make type arrive.

GSAP SplitText gives us characters, words, and lines as animatable units. Below: four canonical reveals used across Bond’s experience work, each with the code it takes to ship.

Variant 01

Character stagger, blur-to-focus

Every glyph enters from a soft blur with a 25ms stagger. Works for display headlines where the reader’s eye should land one letterform at a time.

Craft is a posture, not a finish.

const split = new SplitText(el, { type: "chars" });

gsap.set(split.chars, {
  opacity: 0, y: 24, filter: "blur(10px)",
});

gsap.to(split.chars, {
  opacity: 1, y: 0, filter: "blur(0px)",
  duration: 0.9, ease: "power3.out",
  stagger: 0.025,
});

Variant 02

Word stagger, slide-up

Each word rises into place from below the baseline, overflow-clipped by the line box. Feels like the words are being set in type, one by one.

Write fewer words. Choose them slowly.

const split = new SplitText(el, { type: "words" });

gsap.set(split.words, {
  opacity: 0, yPercent: 110,
});

gsap.to(split.words, {
  opacity: 1, yPercent: 0,
  duration: 0.85, ease: "expo.out",
  stagger: 0.06,
});

Variant 03

Line stagger, clip-path mask

SplitText lines, then a clip-path inset that wipes left-to-right across each line with a long ease. A classic editorial reveal for body copy and quotes.

The best motion never announces itself. You notice the room became warmer, the type settled into place, and somehow the page you landed on felt like it had been there the whole time, waiting.

const split = new SplitText(el, { type: "lines" });

gsap.set(split.lines, {
  clipPath: "inset(0 100% 0 0)",
});

gsap.to(split.lines, {
  clipPath: "inset(0 0% 0 0)",
  duration: 1.1, ease: "power4.out",
  stagger: 0.18,
});

Variant 04

Character scramble, resolve to final

A glyph-by-glyph scramble that resolves left-to-right over 1.4 seconds. Use sparingly — it announces itself. Good for section keyframes, bad for body copy.

Type that arrives with intent.

const glyphs = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/";

function tick(now) {
  const p = Math.min(1, (now - start) / 1400);
  const locked = Math.floor(p * target.length);
  const out = target.split("").map((ch, i) =>
    i < locked ? ch : glyphs[rand(glyphs.length)]
  ).join("");
  setDisplay(out);
  if (p < 1) requestAnimationFrame(tick);
}

How this is built

One library. Four dialects.

SplitText does the dirty work — segmenting a string into animatable DOM nodes without breaking layout. From there it’s restraint: choose the right unit, choose the right ease, keep the stagger short, and always give reduced-motion users the finished string on first paint.

01GSAP SplitTextOne library, four primitives — chars, words, lines, and combinations thereof. Non-destructive: split.revert() restores the original DOM.View source
02IntersectionObserver triggerEach variant uses a shared useInView hook with a -10% bottom margin. Animations fire on 30–35% visibility, never on load, never twice.View source
03Char stagger, blur-to-focusgsap.set + gsap.to on split.chars with a 25ms stagger and a filter: blur(10px) → blur(0px) transition. Editorial, not gimmicky.View source
04Word stagger, slide-upsplit.words with yPercent: 110 → 0 under an expo.out ease. The line-box clipping creates a letterpress feel without any extra markup.View source
05Line mask via clip-pathsplit.lines with clipPath: inset(0 100% 0 0) → inset(0 0% 0 0). Left-to-right wipe, 180ms stagger, 1.1s power4.out ease.View source
06Character scramblerequestAnimationFrame-driven. Each frame resolves floor(progress × length) characters and replaces the rest with random glyphs from an alphabet.View source
07Reduced-motion fallbackWhen prefers-reduced-motion: reduce is set, every variant skips the split and renders the final string verbatim. Readable on the first paint.View source
08Accessibility: aria-labelThe scramble variant carries an aria-label with the final string so assistive tech reads intent, not noise. SplitText output is visual only.View source
Back to the showcase