What GSAP is (and why it's famous)
GSAP (GreenSock Animation Platform) is a JavaScript library for animating anything — HTML, SVG, canvas, WebGL, React props, even arbitrary JS values. It's famous because it's the fastest, most reliable animation tool on the web, with an API that reads like plain English.
CSS animations vs. GSAP — when to reach for each
Use CSS when
- Simple hover, focus, or state transitions
- Animation runs once and doesn't need sequencing
- You want zero JavaScript overhead
- The animation is tied to user interaction (hover, focus)
Use GSAP when
- You need to sequence multiple animations (timeline)
- Animations depend on scroll position
- You need to animate SVG morphs, paths, or 3D transforms smoothly
- You want pause, reverse, restart controls
- You're staggering lots of elements
- Performance & consistency across browsers matters
Installing GSAP — one line
The CDN is the easiest way to start. Paste one script tag, and gsap
is available globally.
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script>
gsap.to('.box', { x: 200, duration: 1 });
</script>npm install gsap
// then in your JS
import gsap from 'gsap';
gsap.to('.box', { x: 200, duration: 1 });<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script> <script> gsap.registerPlugin(ScrollTrigger); </script>
gsap.to() — the one method you'll use most
Click the button to animate the box. Click it again to watch it re-run.
gsap.to() means "animate these elements to these
values over this duration."
<div class="box"></div> <button onclick="animate()">Run</button>
function animate() {
gsap.to('.box', {
x: 200, // move 200px to the right
rotation: 360, // spin once
scale: 1.4, // grow 40%
backgroundColor: '#ec4899',
duration: 1, // over 1 second
ease: 'power2.out', // smooth deceleration
yoyo: true, // come back
repeat: 1 // once is enough
});
}Stagger — animate lists with one line
stagger tells GSAP to delay each element by a bit, giving you
a cascading effect for free. This would be tedious in raw CSS.
gsap.to('.box', {
y: 0,
opacity: 1,
duration: 0.6,
ease: 'back.out(1.7)',
stagger: 0.08 // each box starts 80ms after the previous
});
// Stagger supports more:
gsap.to('.grid-item', {
scale: 1,
stagger: {
each: 0.05,
from: 'center', // 'start' | 'end' | 'center' | 'edges' | 'random'
grid: [6, 6] // treat as a 6×6 grid
}
});Timelines — choreograph multiple animations
A gsap.timeline() lets you sequence animations one after
another, or overlap them, without calculating delays by hand. This is where GSAP really
pulls ahead of raw CSS.
const tl = gsap.timeline();
tl.to('.box', { x: 300, duration: 0.8, ease: 'power2.inOut' })
.to('.box', { rotation: 360, duration: 0.6 })
.to('.box', { y: -40, duration: 0.3, yoyo: true, repeat: 1 })
.to('.box', { x: 0, duration: 0.6, ease: 'power2.inOut' })
.to('.text', { opacity: 1, y: -10, duration: 0.4 }, '-=0.2');
// The '-=0.2' at the end overlaps the last tween by 0.2s
// (start 0.2s before the previous animation ends)
// Timelines expose controls:
tl.pause();
tl.reverse();
tl.restart();
tl.progress(0.5); // jump to 50%Animating text, letter by letter
Split a string into characters, then animate each one with stagger.
This is how every award-winning hero section works.
const target = document.getElementById('splitText');
const text = 'Hello, animation.';
// Wrap each character in its own span
target.innerHTML = text
.split('')
.map(c => c === ' '
? ' '
: `<span class="char">${c}</span>`
).join('');
gsap.from('.char', {
y: 40,
opacity: 0,
rotation: 12,
duration: 0.6,
ease: 'back.out(2)',
stagger: 0.04
});ScrollTrigger — animate as the user scrolls
ScrollTrigger is GSAP's most famous plugin. It fires animations tied to scroll position — fade in on enter, pin elements, progress bars, parallax. Scroll down to see it fire.
This box appears when you scroll to it
When 80% of the viewport passes this element, GSAP fades it in, rotates it, and scales it up. If you scroll back past it, it resets.
gsap.registerPlugin(ScrollTrigger);
gsap.from('.scroll-box', {
scale: 0.2,
rotation: -180,
opacity: 0,
duration: 1,
ease: 'power3.out',
scrollTrigger: {
trigger: '.scroll-box',
start: 'top 80%', // fires when top of box hits 80% down viewport
end: 'bottom 20%',
toggleActions: 'play none none reverse'
// actions for: onEnter | onLeave | onEnterBack | onLeaveBack
}
});
gsap.from('.scroll-text', {
x: 80,
opacity: 0,
duration: 1,
scrollTrigger: { trigger: '.scroll-text', start: 'top 80%' }
});scrub: true to make the animation
scrub with the scrollbar, not just trigger once. That's how scroll-driven video-like
storytelling works.
Easing — the soul of an animation
The same animation with a different ease can feel completely
different. GSAP ships with dozens of curves — here are the ones you'll actually use:
// Linear — straight line. Rarely looks natural. ease: 'none' // Power (most common) — accelerates or decelerates ease: 'power1.out' // subtle ease: 'power2.out' // moderate (default) ease: 'power3.out' // strong ease: 'power4.out' // dramatic // Back — overshoots the target ease: 'back.out(1.7)' // classic "bounce past then settle" // Elastic — wobbles around the target ease: 'elastic.out(1, 0.3)' // Bounce — physical ball bounce ease: 'bounce.out' // Custom — your own curve ease: 'cubic-bezier(.17,.67,.83,.67)' // Browse all at: greensock.com/ease-visualizer
The whole GSAP core in 30 lines
// Animate TO these values
gsap.to('.box', { x: 200, duration: 1 });
// Animate FROM these values (to the CSS default)
gsap.from('.box', { y: -80, opacity: 0, duration: 0.6 });
// Animate FROM and TO (both ends specified)
gsap.fromTo('.box',
{ opacity: 0, y: 20 },
{ opacity: 1, y: 0, duration: 0.8 }
);
// Set values instantly (no tween)
gsap.set('.box', { scale: 1.2 });
// Kill all running animations on an element
gsap.killTweensOf('.box');
// Timeline — sequence animations
const tl = gsap.timeline({ repeat: -1, yoyo: true });
tl.to('.a', { x: 200 })
.to('.b', { y: 100 }, '<') // start with previous
.to('.c', { rotation: 90 }, '+=0.5'); // 0.5s after previous ends
// Stagger across a group
gsap.to('.item', { opacity: 1, stagger: 0.05 });
// ScrollTrigger — tie to scroll
gsap.to('.hero', {
y: 100,
scrollTrigger: {
trigger: '.hero',
start: 'top top',
end: 'bottom top',
scrub: true
}
});