← BACK TO BLOG

The GSAP 80/20 Guide

Introduction

When building creative websites, I realized that you really don't need that many techniques. Master a handful of core features and you can nail most of the jaw-dropping animations out there, that's the 80/20 rule of web animation. In this article, I'll walk through GSAP's most essential core feature: Timeline, and its most powerful plugin: ScrollTrigger. Master these two, and you can recreate the majority of effects you see on sites featured in the GSAP Showcase (gsap.com/showcase), most of them leverage ScrollTrigger, and ScrollTrigger paired with Timeline is the perfect combo.

However, even though these tools look easy to learn and seem universally applicable, the hidden pitfalls in real-world projects are no joke. Below, I've also compiled the traps I've personally fallen into.

✦✦✦

The Underlying Principle of Web Animation

The vast majority of flashy interactions on the web, when you break them down, are just a game of value mapping.

We take user input (time, mouse position, scroll velocity) and convert it into a 0 to 1 value, then apply that value to an element's visual properties (like transform, opacity, clip-path). In between, we smooth things out with lerp, spring, or easing curves to make the motion feel fluid and natural.

The Real Pattern

Whether it's scroll-linked animation or mouse-following effects, every top-tier visual follows this underlying loop:

  1. Read Input: mouse, scroll, time
  2. Normalize it: map to a useful 0 to 1 range
  3. Apply easing: lerp, springs, easing curves
  4. Move stuff: transform, opacity, clip-path

This principle is implemented through the GSAP features introduced below. Once you grasp it, you can conjure endless variations.

✦✦✦

Timeline: Use Cases & How It Works

To understand Timeline, you first need to know what a Tween is. A Tween is the smallest animation unit in GSAP, derived from traditional animation's "In-between" (tweening). You declare an object's start or end state, and GSAP's internal Ticker automatically fills in all the intermediate values. It controls not just common properties like opacity and scale, but also advanced ones like clip-path (mask clipping) and filter: blur (blur filters).

When multiple Tweens are combined, they evolve into a Timeline. In the old days, chaining sequential animations meant manually adding different delay values. Tweak one by 0.1 seconds and dozens of lines downstream would collapse. Timeline is an auto-sequencing container: just drop Tweens in order and they play one after another. With Timeline parameters like "-=0.5" (enter 0.5s early) or "<" (start with the previous), you can break free from rigid linear playback and create breathing, natural visuals.

Code Example

const tl = gsap.timeline();

tl.to(".hero-title", { y: 0, opacity: 1, duration: 1 })
  .to(".hero-bg-img", { scale: 1 }, "-=0.4")   // Background starts scaling 0.4s before title finishes
  .from(".nav-item", { opacity: 0 }, "<");       // Nav fades in at the exact same moment as background

Pitfall Guide

1. Never animate the same property with both GSAP and CSS

When GSAP's Ticker is force-updating an element's style every 16ms, your CSS transition will try to intercept those values. The two systems fight each other at the browser level, causing the element to jitter and twitch violently. If an element is controlled by GSAP, remove any CSS transition on that same property. Split the properties so each system owns its own, or use GSAP's onComplete callback to hand control back to CSS after the animation finishes.

2. Properties GSAP shouldn't animate

top, left, margin, width, padding are layout properties. Animating them forces the browser to recalculate every element's position (triggering reflow), turning smooth animations into choppy stutters with CPU spikes. For position changes, use x and y (which map to CSS transform: translate()). Properties without mathematically continuous intermediate values, like switching from display: none to block, or position: absolute to fixed, are physically impossible for GSAP to tween. Use opacity, autoAlpha, or advanced plugins instead.

✦✦✦

ScrollTrigger Viewport: Use Cases & How It Works

This links animations to whether an element has entered the visible viewport, like an automatic door on a webpage: walk past it and it opens; walk away and it closes.

Imagine two moving containers on your page:

  1. The Viewport: It moves as you scroll and carries two detection lines, Viewport Start and Viewport End.
  2. The Trigger Element: It's an element on the page, also carrying two trigger lines, Element Start and Element End.

Animation triggering is the moment these lines cross. When the viewport's detection line scrolls to a position where it meets the element's trigger line, the animation begins (Start). When their end lines cross, the performance concludes (End).

Beyond setting start and end trigger points, toggleActions lets you decide whether the animation plays, reverses, or resets when entering, leaving, or re-entering. It defines behaviors for four line-crossing events: onEnter (Viewport Start meets Element Start), onLeave (Viewport End meets Element End), onEnterBack (Viewport End backs into Element End), and onLeaveBack (Viewport Start meets Element Start again). If the line concept feels confusing, set markers: true to visualize them directly on the page.

Code Example

gsap.from(".portfolio-card", {
  y: 50,
  scrollTrigger: {
    trigger: ".portfolio-card",
    start: "top 80%",    // Triggers when element's top hits viewport's 80% mark
    end: "bottom 20%",   // Ends when element's bottom hits viewport's 20% mark
    markers: true,       // Show debug markers on the page
  }
});

Pitfall Guide

1. Element's start point is above the page top

For Hero Section elements that need to animate on page load, don't use ScrollTrigger. Use a plain gsap.timeline() that fires immediately on load. Only use ScrollTrigger for elements below the first screen.

2. Mobile browser toolbar resizing causes jitter

When scrolling on mobile, the URL bar and bottom toolbar auto-collapse, causing the viewport height (100vh) to suddenly change. ScrollTrigger recalculates all detection lines to maintain accuracy, producing visible jumps. Fix: add ScrollTrigger.config({ ignoreMobileResize: true }) at initialization to tell GSAP to ignore minor height changes from toolbar toggling.

3. Lazy loading kills your animation

Modern sites commonly lazy-load images for performance. But when an image finishes loading mid-scroll, it can overwrite GSAP's initial animation frame, causing a harsh flash with no smooth transition. Never lazy-load Hero images on the first screen. For images below the fold, attach ScrollTrigger only after the image's onload event fires, ensuring animation and loading sequence are perfectly synced.

✦✦✦

ScrollTrigger Scrub: Use Cases & How It Works

If Viewport is the automatic door, then Scrub is the remote control in your hand. It's progress-based binding: scroll down a little, the animation advances a little. You control the pace.

The key difference from Viewport: Viewport just detects when two lines collide, then the animation plays on its own timeline, even if your finger stops, the animation keeps going. Scrub, on the other hand, binds the scroll position 100% to the animation timeline's progress.

Scrub's best friend is pin, ScrollTrigger's property for pinning elements. Normally, everything scrolls off-screen as you scroll down. But with pin: true, GSAP internally applies position: fixed to the element, locking it in place on screen, forcing the user to watch the show before they can scroll further.

Code Example

// In scrub mode, the tween's "duration" is irrelevant
// scroll distance IS the timeline!
gsap.to(".scrolling-car-parts", {
  x: 200,
  rotation: 180,
  scale: 0.8,
  ease: "none",           // Always use ease: "none" for scrub
  scrollTrigger: {
    trigger: ".product-section",
    start: "top top",
    end: "+=1500",         // 1500px of scroll distance = animation progress bar
    pin: true,             // Pin the section, user must scroll through the show
    scrub: 1.2,            // 1.2s of physical smoothing/inertia
  }
});

Pitfall Guide

1. Scrub end of range speed burst

If your scroll range (start to end distance) is too short (e.g., 100px) but the element travels a long physical distance (e.g., x: 500), GSAP will rush to catch up near the end boundary, creating an ugly acceleration burst. Fix: widen the scroll range and increase the scrub value.

2. Pin breaks inside overflow containers

GSAP's pin: true uses position: fixed under the hood. But per CSS spec, if any parent has overflow-x: clip or overflow: hidden, it creates a Containing Block that traps position: fixed, stripping away its full-screen lock and causing layout shifts. Fix: ditch GSAP's pin and use native CSS position: sticky combined with GSAP for the animation only.

✦✦✦

Conclusion

In the world of Creative Coding, true masters aren't the ones who memorized every API in GSAP's documentation. They're the ones who can use core tools to choreograph interactions that are both stunning and user-friendly.

But I've also realized that whether you're orchestrating with Timeline or controlling animations through ScrollTrigger's logic, what matters most is having a foundational understanding of browser-level science: Reflow, Containing Blocks, network download speed variations, so you can push these building blocks into endless creative possibilities.