Skip to content
Frontend
Skill

/gsap-web

This skill should be used when the user asks to "build a scroll animation", "add ScrollTrigger", "pin a section while scrolling", "scrub an animation to scroll", "create a hero timeline", "do a horizontal scroll section", "split text and animate words", "animate a layout change

BOOST
From plugin
web-animation-skills
459 skills
Install
$ npx -y skills add iart-ai/web-animation-skills --skill gsap-web --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/gsap-web

Context preview

The summary Claude sees to decide when to auto-load this skill.

This skill should be used when the user asks to "build a scroll animation", "add ScrollTrigger", "pin a section while scrolling", "scrub an animation to scroll", "create a hero timeline", "do a horizontal scroll section", "split text and animate words", "animate a layout change

SKILL.md

gsap-web.SKILL.md
name: gsap-web
description: This skill should be used when the user asks to "build a scroll animation", "add ScrollTrigger", "pin a section while scrolling", "scrub an animation to scroll", "create a hero timeline", "do a horizontal scroll section", "split text and animate words", "animate a layout change with GSAP Flip", "sync ScrollTrigger with Lenis", "Lenis smooth scroll jittery", "ScrollTrigger markers drift", "ScrollTrigger pins break with Lenis/Locomotive", or "set up smooth scroll with GSAP pinning". Covers GSAP timelines, ScrollTrigger, SplitText, Flip, and Lenis/Locomotive smooth-scroll sync for code-driven web motion.
version: 0.1.0

GSAP for the Web

GSAP (GreenSock Animation Platform) is the workhorse for code-driven web motion: sequenced timelines, scroll-driven storytelling, text reveals, and layout transitions. As of GSAP 3.12+, every plugin (ScrollTrigger, SplitText, Flip, MotionPath, MorphSVG, Draggable, Observer) is 100% free, including for commercial use.

When to use

  • Scroll-driven storytelling: pinned sections, parallax, progress scrubbing, horizontal scroll
  • Sequenced hero animations and complex multi-element timelines with precise overlap control
  • Text reveals split into chars/words/lines (SplitText)
  • Layout transitions where an element changes position/size/parent (Flip)
  • Any motion needing fine timing control, easing precision, or imperative orchestration

Install and register

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

gsap.registerPlugin(ScrollTrigger, SplitText, Flip);

Plugins MUST be registered before use or they silently no-op. With a bundler, register once at app entry.

Core techniques

Timelines first

Prefer one timeline over many independent tweens — it gives a single playhead, relative positioning, and easy reversal.

const tl = gsap.timeline({ defaults: { ease: "power3.out", duration: 0.6 } });
tl.from(".title", { yPercent: 100, opacity: 0 })
  .from(".sub",   { y: 20, opacity: 0 }, "-=0.3")  // start 0.3s before prev ends
  .from(".cta",   { scale: 0.9, opacity: 0 }, "<"); // align to prev tween START

Position parameter cheat sheet:

  • `"+=0.5"` / `"-=0.3"` — relative to the end of the timeline (gap / overlap)
  • `"<"` — start of the previous tween; `">"` — end of the previous tween
  • `"<0.2"` — 0.2s after the previous tween's start
  • `"myLabel"` — at a named label added via `tl.addLabel("myLabel")`

Use `.from()` for entrances (animates FROM the given values TO current CSS), `.to()` for exits, `.fromTo()` when both ends must be explicit (most robust against re-runs).

gsap.to vs set vs quickTo

gsap.set(el, { autoAlpha: 0 });           // instant, no tween (autoAlpha = opacity + visibility)
const xTo = gsap.quickTo(el, "x", { duration: 0.4, ease: "power3" });
window.addEventListener("pointermove", (e) => xTo(e.clientX)); // fast repeated updates

`autoAlpha` is preferred over raw `opacity` because it also toggles `visibility:hidden` at 0, removing the element from hit-testing.

ScrollTrigger basics

gsap.to(".panel", {
  xPercent: -100,
  ease: "none",
  scrollTrigger: {
    trigger: ".wrap",
    start: "top top",      // when trigger top hits viewport top
    end: "+=2000",         // 2000px of scroll distance
    pin: true,             // freeze .wrap while the tween plays
    scrub: 1,              // tie progress to scrollbar (1 = 1s catch-up smoothing)
    markers: true,         // dev-only visual markers — remove for prod
  },
});

Key semantics:

  • `start`/`end` take `"triggerPos viewportPos"` (e.g. `"top center"`) or `"+=px"`.
  • `scrub: true` locks animation progress to scroll exactly; `scrub: <number>` adds smoothing lag.
  • `toggleActions: "play pause resume reverse"` controls onEnter/onLeave/onEnterBack/onLeaveBack for non-scrubbed triggers.
  • Use `ease: "none"` for scrubbed tweens so motion tracks scroll linearly.

Smooth scroll (Lenis) + ScrollTrigger

Pairing Lenis (or Locomotive) smooth scrolling with ScrollTrigger desyncs unless both run on one loop. Lenis smooths scroll on its own rAF while ScrollTrigger reads scroll on GSAP's ticker; when they tick independently ScrollTrigger samples a stale position. The fix: drive Lenis from GSAP's ticker and update ScrollTrigger on every Lenis scroll.

import Lenis from "lenis";

const lenis = new Lenis({ duration: 1.2, smoothWheel: true });

lenis.on("scroll", ScrollTrigger.update);          // 1. update ScrollTrigger on every Lenis scroll
gsap.ticker.add((t) => lenis.raf(t * 1000));       // 2. one loop: ticker drives Lenis (seconds → ms)
gsap.ticker.lagSmoothing(0);                        // 3. stop GSAP catch-up jitter on heavy frames

Critical: `gsap.ticker` passes time in **seconds**, `lenis.raf()` wants **milliseconds** — multiply by 1000. Do NOT also run a standalone `requestAnimationFrame(raf)` loop for Lenis; that double-drives it.

Common bugs:

  • **Jitter/stutter** — a leftover `requestAnimationFrame(raf)` loop competing with the ticker, or missing `lagSmoothing(0)`. Remove the rogue loop; set lag smoothing to 0.
  • **Markers drift** from their triggers — `ScrollTrigger.update` is not subscribed to `lenis.on("scroll", …)`.
  • **Pins break** — under Lenis do NOT set a `scrollerProxy` (it scrolls the document). Under Locomotive you MUST wire `scrollerProxy` + `pinType` and set `scroller:` on every trigger.
  • **Scroll stuck / flipped direction** — missing the `* 1000` unit conversion, or two loops out of order.

Cleanup on SPA unmount: `gsap.ticker.remove(update)` + `lenis.destroy()`, else loops stack per navigation. Gate behind `prefers-reduced-motion` (skip Lenis, run native scroll). Full Lenis + Locomotive wiring, anchor-link routing, `data-lenis-prevent`, React (`useGSAP`/`useEffect`), and a symptom→cause table are in `references/scrolltrigger-lenis.md`.

SplitText (text reveal)

``

Read more
Ships withweb-animation-skills

Web animation skills for AI coding agents — GSAP, SVG, Lottie, micro-interactions, page transitions, and 60fps performance, installed straight into your agent.

Get the whole plugin

Other skills on web-animation-skills.