gsap-web
iart-ai/web-animation-skills · Agent Skill
GSAP guidance for code-driven web motion, including timelines, ScrollTrigger, SplitText, Flip, and smooth-scroll synchronization.
Jump to install ↓Overview
| Publisher | |
|---|---|
| Repository | iart-ai/web-animation-skills ↗ |
| Skill folder | gsap-web |
| Stars | 58 |
| Forks | 9 |
| Bundled files | 6 |
| License | MIT |
| Last updated | June 22, 2026 |
| Links | Skill source ↗ · Repository ↗ |
Mozgi check
What we found in the skill files. Details come with a subscription.
Installation
A skill is a folder with a SKILL.md file. Pick your agent and run the commands in a terminal (macOS, Linux or Git Bash on Windows) from your project root.
Copy the prompt into Claude, Codex or another assistant: it will review the skill and install it for you.
Install the “gsap-web” skill from mozgi.io (https://mozgi.io/skills/iart-ai-gsap-web/) for me. 1. Download the archive: https://mozgi.io/skills/iart-ai-gsap-web/download If it fails, take the skill folder from the source: https://github.com/iart-ai/web-animation-skills/tree/main/skills/gsap-web 2. Read SKILL.md and the other files. Check the skill is safe: no hidden commands, no sending data elsewhere, no scripts doing something other than described. If anything looks suspicious, stop and tell me. 3. Put the “gsap-web” folder into your agent's skills folder: .claude/skills/ for Claude Code, .agents/skills/ for Codex, .cursor/skills/ for Cursor, .gemini/skills/ for Gemini CLI. Inside the project if we are in one, otherwise in the home folder (~/.claude/skills/ etc.). 4. Make sure <skills folder>/gsap-web/SKILL.md exists. 5. Briefly tell me what this skill does and offer to try it on my task. If you cannot do a step yourself, tell me what to click or type.
git clone --depth 1 --branch main https://github.com/iart-ai/web-animation-skills.git /tmp/web-animation-skills mkdir -p .claude/skills cp -r /tmp/web-animation-skills/skills/gsap-web .claude/skills/gsap-web
Restart Claude Code — the skill appears in the list; call it manually with /gsap-web. For all projects use ~/.claude/skills.
git clone --depth 1 --branch main https://github.com/iart-ai/web-animation-skills.git /tmp/web-animation-skills mkdir -p .agents/skills cp -r /tmp/web-animation-skills/skills/gsap-web .agents/skills/gsap-web
Codex looks for skills in .agents/skills from the current folder up to the repo root; call one with /skills or $gsap-web. For all projects use ~/.agents/skills.
git clone --depth 1 --branch main https://github.com/iart-ai/web-animation-skills.git /tmp/web-animation-skills mkdir -p .cursor/skills cp -r /tmp/web-animation-skills/skills/gsap-web .cursor/skills/gsap-web
Cursor picks the skill up on start; call it with “/” in Agent chat. Cursor also reads .claude/skills and .agents/skills. For all projects use ~/.cursor/skills.
git clone --depth 1 --branch main https://github.com/iart-ai/web-animation-skills.git /tmp/web-animation-skills mkdir -p .gemini/skills cp -r /tmp/web-animation-skills/skills/gsap-web .gemini/skills/gsap-web
Gemini CLI offers to activate the skill when a task matches — you approve it. For all projects use ~/.gemini/skills.
Read SKILL.md and any companion files before deciding whether to install. Click a file to preview it.
SKILL.md contents
Original author text · read-onlyGSAP 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 viatl.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/endtake"triggerPos viewportPos"(e.g."top center") or"+=px".scrub: truelocks 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 missinglagSmoothing(0). Remove the rogue loop; set lag smoothing to 0. - Markers drift from their triggers —
ScrollTrigger.updateis not subscribed tolenis.on("scroll", …). - Pins break — under Lenis do NOT set a
scrollerProxy(it scrolls the document). Under Locomotive you MUST wirescrollerProxy+pinTypeand setscroller:on every trigger. - Scroll stuck / flipped direction — missing the
* 1000unit 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)
const split = SplitText.create(".headline", { type: "lines, words", linesClass: "line" });
gsap.from(split.lines, { yPercent: 100, opacity: 0, stagger: 0.08, duration: 0.7, ease: "power4.out" });
Gotchas:
- Wrap line-mask reveals: set
overflow: hiddenon the line wrapper soyPercent: 100hides cleanly. AddautoSplit: true(GSAP 3.13+) to re-split on font load / resize. - Call
split.revert()before re-splitting or on unmount to restore original DOM and avoid duplicated nodes. - Always split AFTER web fonts load (
document.fonts.ready.then(...)) to prevent wrong line breaks.
Flip (layout transitions)
Flip records state, lets the DOM change instantly, then animates the visual difference (FLIP technique). Ideal for grid<->list, expanding cards, and reparenting.
const state = Flip.getState(".item"); // 1. capture BEFORE
container.classList.toggle("grid"); // 2. mutate DOM/CSS (instant)
Flip.from(state, { // 3. animate the delta
duration: 0.6, ease: "power2.inOut", stagger: 0.05,
absolute: true, // take items out of flow during move (prevents reflow jitter)
});
Easing quick guide
power2/3.out— entrances (fast then settle)power2.inOut— moves between two on-screen statesback.out(1.7)— overshoot/pop (number = overshoot amount)elastic.out(1, 0.3)— bouncy, use sparinglynone— scrubbed scroll tweenssteps(n)— sprite/stepped motion- Custom cubic-bezier equivalent:
CustomEase.create("x", "M0,0 C0.2,0 0,1 1,1")(CustomEase is free)
Performance and cleanup
- Animate
transform(x/y/xPercent/scale/rotation) andopacityonly — they are GPU-composited and skip layout/paint. Avoid animatingtop/left/width/height. - Set
will-change: transformon heavy/pinned elements; remove after. - Batch many similar scroll reveals with
ScrollTrigger.batch()instead of one trigger per element. - After a viewport/content change, call
ScrollTrigger.refresh().
SPA / framework cleanup is mandatory — orphaned triggers cause memory leaks and ghost pinning. Use gsap.context() (or useGSAP from @gsap/react):
useEffect(() => {
const ctx = gsap.context(() => {
// all gsap + ScrollTrigger code here
}, rootRef);
return () => ctx.revert(); // kills tweens, triggers, and reverts inline styles
}, []);
Deliver & verify (standalone HTML)
> Packaged helper (scripts/): scripts/seek-shot.sh anim.html 0 1.5 3 freezes the ?t=N harness and screenshots each moment; scripts/contact-sheet.sh sheet.png frame-*.png tiles them for one-glance review. See scripts/README.md.
For a self-contained animation (hero, reveal, loop, micro-scene) the deliverable is one HTML file that opens directly in a browser — no build step, no framework, no render pipeline. Match the deliverable to the weight of the work: a single file is the right tier for web motion; don't reach for a bundler when one file does the job.
Output contract:
- One
.htmlfile: GSAP + plugins from CDN, your markup, and the animation in one inline<script>. - All motion on one master timeline (
const tl = gsap.timeline()) — a single playhead you can seek. - Include the seek harness below so any moment can be frozen for inspection.
Seek harness — freeze an exact moment for screenshots. The web parallel of a video player's frame-pin: ?t=N seeks the master timeline to N seconds and pauses, so a screenshot lands on a still, deterministic frame.
<script>
// ... build your master timeline as `tl` ...
const t = new URLSearchParams(location.search).get("t");
if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); } // frozen at t seconds
// no ?t → plays normally
window.__ready = true; // ready signal for headless wait
console.log("duration", tl.duration());
</script>
Verify loop — render → freeze → screenshot → check:
1. Open the file at three moments across the timeline — start, mid, end:
…/anim.html?t=0, ?t=<dur/2>, ?t=<dur>. Read tl.duration() from the console for the end time.
2. Screenshot each frozen frame.
3. Check both fidelity (does it match the brief?) and artifacts (clipped text, elements off-canvas, FOUC before fonts load, jank at seams). Output should look intentional and finished.
Any headless screenshot tool works — your agent's browser tool, or Playwright:
npx playwright screenshot --wait-for-timeout=500 "file://$PWD/anim.html?t=1.2" frame-mid.png
Before you finish:
1. Opens standalone in a browser — no console errors, no missing CDN.
2. All animation on one master timeline; ?t=N freezes correctly.
3. Screenshotted at start / mid / end — matches the brief, no artifacts.
4. prefers-reduced-motion honored (timeline simplified or skipped).
5. Easing is intentional — no accidental linear on spatial motion.
A complete runnable template with the harness wired in is in examples/standalone-template.html.
Quick reference
| Goal | API |
|------|-----|
| Sequence with overlap | tl.from(...).from(..., "-=0.3") |
| Pin while scrolling | scrollTrigger:{ pin:true, end:"+=N" } |
| Scrub to scroll | scrollTrigger:{ scrub:1, ease:"none" } |
| Reveal lines of text | SplitText.create(el,{type:"lines"}) |
| Animate layout change | Flip.getState → mutate → Flip.from |
| Fast pointer follow | gsap.quickTo(el,"x",{...}) |
| Kill everything | ScrollTrigger.getAll().forEach(t=>t.kill()) |
Reference files
references/scrolltrigger-cookbook.md— pin, scrub, horizontal scroll, snap,ScrollTrigger.batch()reveals, parallax, nested triggers, and SPA cleanup patterns with full code.references/scrolltrigger-lenis.md— full ScrollTrigger + Lenis/Locomotive smooth-scroll sync: canonical wiring,scrollerProxy, anchor links, reduced-motion, React (useGSAP/useEffect), debugging checklist, and symptom→cause table.examples/hero-timeline.js— a complete, runnable hero entrance timeline with SplitText, staggered reveals, and reduced-motion handling.examples/standalone-template.html— the deliverable template: self-contained (CDN GSAP), one master timeline, the?t=Nseek harness for screenshot verification, and reduced-motion handling.
Frequently asked questions
What does the gsap-web skill do?
GSAP guidance for code-driven web motion, including timelines, ScrollTrigger, SplitText, Flip, and smooth-scroll synchronization.
How do I install gsap-web?
Open the “Installation” block on this page and pick your agent — Claude Code, Codex, Cursor or Gemini CLI — for ready-made commands that copy the skill folder to the right place. Or copy the prompt from the “Via assistant” tab and let the assistant review and install the skill.
Which AI models work with gsap-web?
Skills are plain Markdown instructions (the Agent Skills format), so gsap-web works with any agent that reads SKILL.md: Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and more. The model loads the instructions only when a task matches the skill's description.
Is gsap-web free?
The skill is published openly by iart-ai under the MIT license. The skill itself costs nothing — you only pay your AI provider for the tokens you use.
Is it safe to install skills?
A skill is a set of instructions your agent will follow, so read SKILL.md and its companion files before installing. Scripts in the skill folder only run if your agent runs them — keep an eye on what it does and do not grant unnecessary permissions.
How is a skill different from an MCP server or a plugin?
An MCP server or plugin gives the model new tools — code that runs and returns a result. A skill gives it knowledge and process: how to approach a task, which steps to follow and what good output looks like. It needs no server or API key.