Skip to content
motionprompts

How to make a text reveal on scroll with GSAP

Guide · Published 2026-10-19 · by vanguardia.dev

A text reveal on scroll splits a heading or paragraph into lines, words or characters, hides each piece just outside a clipping mask and animates it into place when the block scrolls into view. With GSAP you need two plugins for it: SplitText cuts the text into elements you can animate, and ScrollTrigger decides when the animation runs or ties its progress to the scrollbar. Both are part of the free GSAP package since version 3.13.

Live example: Text Reveal Animation · Open live demo ↗

The technique in three decisions

What to split. Lines are the most readable choice for paragraphs: the text arrives as it will be read and nothing jumps around once it settles. Words suit short statements that you want to light up as the reader moves through them. Characters only work for short display headlines; on a paragraph they turn into noise and slow the page down.

How to hide it. The classic look is a mask: each line is wrapped in an element with overflow: clip, and the line itself starts pushed down by its own height, so it sits just below the visible slot. Animating it back to zero makes the text rise out of nowhere. Opacity, blur or a skew can be layered on top, but the mask is what gives the clean editorial feel.

When it plays. A triggered reveal fires once when the block crosses a point in the viewport and then stays put. A scrubbed reveal maps the scroll position to the animation progress, so it moves backwards when the reader scrolls up. Triggered reveals are calmer and better for long reading pages; scrubbed ones work for a single statement in a pinned section.

A minimal version

This is the smallest reliable version of a masked line reveal. It waits for the web fonts, splits each element marked with data-reveal into masked lines and lets SplitText rebuild the split when the width changes.

import gsap from "gsap";
import { SplitText } from "gsap/SplitText";
import { ScrollTrigger } from "gsap/ScrollTrigger";

gsap.registerPlugin(SplitText, ScrollTrigger);

document.fonts.ready.then(() => {
  document.querySelectorAll("[data-reveal]").forEach((el) => {
    SplitText.create(el, {
      type: "lines",
      mask: "lines",
      autoSplit: true,
      onSplit: (self) =>
        gsap.from(self.lines, {
          yPercent: 100,
          duration: 0.9,
          stagger: 0.08,
          ease: "power4.out",
          scrollTrigger: { trigger: el, start: "top 80%", once: true },
        }),
    });
  });
});

Returning the tween from onSplit matters: when the layout changes and the text is split again, SplitText reverts the old animation and starts the new one from the same point, so the masks never cut through the middle of a word. To scrub the reveal instead, drop once and add scrub: true with an end value, and split by words if the block is a single sentence.

Add one line of CSS so the text is not visible for a frame before the script runs: give [data-reveal] visibility: hidden and set it back to visible as the first step of onSplit.

Where it gets hard

Fonts. Lines are measured when the text is split. If the fallback font is still showing, the line breaks land in different places and the real font then overflows the masks. Waiting for document.fonts.ready fixes most cases; for a font that loads late on purpose, split after that specific face has loaded.

Resizing. Every width change can move the line breaks. Without autoSplit, a reveal that looked perfect on a laptop leaves broken masks after rotating a phone. If you build the split yourself, debounce the resize, revert the split, split again and recreate the trigger.

Smooth scrolling. With Lenis or another smooth scroller, ScrollTrigger has to read the same scroll position the page is showing. Connect them once at start up by calling ScrollTrigger.update from the scroller's scroll event, or the triggers fire late and the reveal looks out of sync.

Layout details. Split lines inherit text indentation, letter spacing and inline styles, and some of them do not survive the split. A first line indent ends up on every line, and inline links can break across two masks. Check the split on the real copy, not on placeholder text.

Accessibility. A screen reader should hear the sentence, not twenty separate fragments. SplitText adds an aria-label with the full text to the parent and hides the pieces from assistive technology by default; keep that behavior. Respect prefers-reduced-motion with a branch that shows the copy immediately, and never leave important text hidden until a scroll event that might not arrive.

Performance

Animate transform and opacity only, and the reveal costs almost nothing even on modest phones. What gets expensive is the number of elements: splitting a long article into characters creates thousands of nodes. Split by lines for body copy, keep characters for headlines, and use once: true so finished triggers stop listening.

Variations in the library

Each one has a live demo and a validated AI prompt that rebuilds it in your project.

Text Reveal Animation

Free prompt

The live example above, with a free prompt. Characters slide in with a slight skew, and one page shows the same reveal on load, on scroll and scrubbed, side by side.

Line-by-Line Text Reveal

Premium prompt

The reference version of the masked line reveal: every heading and paragraph opts in through a data attribute, so new copy gets the effect with markup alone.

Scroll Animated Text Reveal

Premium prompt

Scrubbed instead of triggered: the section pins and each word fills in as the reader scrolls, with a few keywords landing inside colored pills.

Gooey Blur Text Reveal

Premium prompt

Lines start as a heavy blur and an SVG threshold filter turns the soft edges into liquid, so the text seems to condense out of ink.

Phive Text Scroll Animation

Premium prompt

Display type for a pinned sequence: giant words stretch open vertically with the scroll and the last one scales up to become a window onto a photo.

ScrollTrigger Variable Font Marquee

Premium prompt

Nothing moves in space here. The scroll drives the weight axis of a variable font, so headings thicken letter by letter between image marquees.

Outline Text Filled by Media

Free prompt

A hollow outlined word over a photograph is wiped into a second copy filled with that same photograph, aligned to the pixel.

Build it with an AI prompt

The snippet above is the bare idea. Text Reveal Animation is the finished, designed version, and its prompt is free: paste it into Claude Code, Cursor or any AI coding agent and it rebuilds the effect in your project with the exact structure, timing and easing.

Get the free prompt Raw prompt (.md)

Frequently asked questions

Is SplitText free?

Yes. Since GSAP 3.13 every plugin, SplitText and ScrollTrigger included, ships in the public gsap package at no cost, also for commercial projects.

Can I do a text reveal without SplitText?

Yes. You can wrap words in spans by hand or with a small library such as SplitType, and CSS scroll driven animations can handle simple cases. What you lose is line detection that updates on resize and the accessibility handling, which are the two parts that are easy to get wrong.

Why do my lines break in the wrong place after the reveal?

The text was split before the final font loaded or before the layout settled. Split after document.fonts.ready, and let autoSplit split again when the width changes.

Does a text reveal hurt SEO?

No, as long as the text is in the HTML. Search engines read the original markup, and SplitText keeps the words in the page. Avoid injecting the copy with JavaScript, and avoid leaving it at zero opacity when the script fails.

Keep going