Plotting the skySuiting upWarming the enginesGo for launch00%

All notes
3 min readGSAPMotion

Why I keep reaching for GSAP

I first used it on a game launch years ago. It's still the animation library I enjoy most, and here's what I've learned along the way.

I first used GSAP on the Saints Row website. The brief was loud: big type, explosions, a site that felt like the game. CSS transitions weren't going to get us there, so we reached for GreenSock. I've used it on almost every project with real motion since, including this one.

It's not the newest tool, and it's not the trendiest. But it does the thing I care about most in animation: it gets out of the way and lets you think about timing.

Timelines beat delays

The first habit GSAP broke for me was chaining animations with delays. Delays are fine until you change one, and then everything after it is out of sync.

A timeline describes the sequence instead:

TypeScript
gsap
  .timeline({ defaults: { ease: "expo.out" } })
  .fromTo(menu, { clipPath: "circle(0% at 94% 4%)" }, { clipPath: "circle(150% at 94% 4%)", duration: 1 })
  .from(links, { yPercent: 110, stagger: 0.06 }, "-=0.5")
  .from(footer, { autoAlpha: 0, y: 16 }, "-=0.7");

That's the mobile menu on this site. The "-=0.5" means "start half a second before the previous step ends", so the links rise while the circle is still opening. If I make the circle slower, everything else follows. And because it's a timeline, closing the menu is just tl.reverse(), played a bit faster.

Easing is most of the feel

If I had to pick one thing that separates motion that feels expensive from motion that feels cheap, it's easing. Almost everything on this site uses expo.out: fast at the start, a long, soft landing. It feels responsive because the movement starts immediately, and calm because it never stops abruptly.

I set it once as the default and only change it when there's a reason to:

TypeScript
gsap.defaults({ ease: "expo.out", duration: 1.1 });

ScrollTrigger and SplitText do the heavy lifting

Most of the text reveals here are three lines of code. SplitText breaks a heading into lines, wraps each one in a mask, and ScrollTrigger plays it when it scrolls into view:

TypeScript
SplitText.create(heading, {
  type: "lines",
  mask: "lines",
  autoSplit: true,
  onSplit: (self) => gsap.from(self.lines, { yPercent: 105, stagger: 0.09, scrollTrigger: { trigger: heading, start: "top 88%" } }),
});

autoSplit re-splits the text when fonts load or the window resizes, which used to be a whole class of bugs I had to handle myself. And since GSAP became free, plugins like SplitText included, there's no reason not to use them.

One clock for everything

This site uses Lenis for smooth scrolling, a 3D scene and a handful of ScrollTriggers. If each of them ran its own requestAnimationFrame loop, they'd drift a frame apart and you'd feel it as jitter. Instead, everything runs off GSAP's ticker:

TypeScript
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);

The astronaut, the stars and the scroll position all update in the same frame, in the same order. It's a small thing that makes the whole page feel solid.

Clean up after yourself

In React, the useGSAP hook is the most underrated part of the library. Everything you create inside it is reverted when the component unmounts, so you don't leave ScrollTriggers behind when you change pages. For anything created later, inside a promise or an event handler, contextSafe wraps it so it's cleaned up too. Before I used this, leaked ScrollTriggers were the most common motion bug I shipped.

Respect people who don't want motion

Last, and most important: some people get dizzy or distracted by animation, and they tell their system so. I check for that before setting anything up, and if they've asked for reduced motion, the text simply appears and the astronaut stays still. Good motion is optional by design.

GSAP doesn't make animations good on its own. But it makes it cheap to try things, change your mind, and get the timing right, and that's where the good stuff comes from.

Say hello

What's it about?