Installation
1. Setup & Installation
Pure HTML/CSS/JS
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
Vite Vanilla JS
npm create vite@latest npm install npm install gsap
Import:
import gsap from "gsap";
GSAP is a powerful JavaScript animation library used to create smooth, high-performance animations for websites. It allows precise control over motion, timing, and sequences, making it ideal for advanced UI and scroll-based effects.
GSAP (GreenSock Animation Platform) is a professional JavaScript animation library used to create:
Official site:
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
npm create vite@latest npm install npm install gsap
Import:
import gsap from "gsap";
<div class="box"></div>
.box {
width: 100px;
height: 100px;
background: red;
}
gsap.to(".box", {
x: 300
});
Animates an element from its current state to a new state.
gsap.to(target, {
property: value,
duration: time
});
Example
<div class="box"></div>
<script>
gsap.to(".box", {
x: 300,
duration: 2
});
</script>
Animates an element from a given state to its current state.
gsap.from(target, {
property: value,
duration: time
});
Example
gsap.from(".box", {
x: -300,
opacity: 0,
duration: 2
});
Defines both starting and ending values.
gsap.fromTo(
target,
{ startProperties },
{ endProperties }
);
gsap.fromTo(
".box",
{
x: 0
},
{
x: 500,
duration: 2
}
);
Changes properties instantly.
gsap.set(target, {
property: value
});
gsap.set(".box", {
x: 200,
opacity: 0.5
});
Immediately:
Set initial state before animation.
gsap.set(".box", { opacity: 0 });
gsap.to(".box", {
opacity: 1,
duration: 2
});
GSAP animates many CSS properties.
x: 200 y: 100
rotation: 360
scale: 2
opacity: 0.5
width: "300px", height: "200px"
backgroundColor: "blue"
gsap.to(".box", {
x: 200,
rotation: 360,
scale: 1.5,
opacity: 0.7,
duration: 2
});
Easing controls animation speed behavior.
ease: "none"
Constant speed.
---------
ease: "power1.out"
Starts fast, ends slow.
------___
ease: "power2.out"
Smoother effect.
gsap.to(".box", {
x: 300,
duration: 2,
ease: "power2.out"
});
ease: "bounce.out"
gsap.to(".ball", {
y: 300,
duration: 2,
ease: "bounce.out"
});
ease: "elastic.out"
Spring-like effect.
gsap.to(".box", {
x: 300,
ease: "elastic.out(1, 0.3)"
});
Repeats animation multiple times.
gsap.to(".box", {
x: 300,
duration: 1,
repeat: 2
});
Run 1 Run 2 Run 3
Because:
repeat: 2
means repeat 2 extra times.
repeat: -1
Example:
gsap.to(".box", {
rotation: 360,
duration: 2,
repeat: -1
});
Infinite rotation.
Makes animation reverse direction after each repeat.
gsap.to(".box", {
x: 300,
duration: 1,
repeat: 3,
yoyo: true
});
→ reset → reset → reset
→ ← → ←
Animates multiple elements one after another.
<div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div>
gsap.to(".box", {
y: -100,
duration: 1,
stagger: 0.3
});
Box1 ---->
0.3s
Box2 ---->
0.3s
Box3 ---->
0.3s
Box4 ---->
gsap.to(".box", {
y: -100,
duration: 1,
stagger: {
each: 0.2,
from: "center"
}
});
Animation starts from center element and spreads outward.
Timelines are one of GSAP's most powerful features. Instead of managing multiple animations separately, you can organize them into a sequence.
Without Timeline:
gsap.to(".box1", {
x: 200,
duration: 1
});
gsap.to(".box2", {
x: 200,
duration: 1,
delay: 1
});
gsap.to(".box3", {
x: 200,
duration: 1,
delay: 2
});
Problems:
With Timeline:
const tl = gsap.timeline();
tl.to(".box1", {
x: 200,
duration: 1
})
.to(".box2", {
x: 200,
duration: 1
})
.to(".box3", {
x: 200,
duration: 1
});
Cleaner and easier to control.
A Timeline is a container that holds multiple animations.
const tl = gsap.timeline();
<div class="box red"></div>
<div class="box blue"></div>
<div class="box green"></div>
const tl = gsap.timeline();
tl.to(".red", {
x: 300,
duration: 1
})
.to(".blue", {
x: 300,
duration: 1
})
.to(".green", {
x: 300,
duration: 1
});
Red --------> Blue --------> Green -------->
Each animation starts after the previous one ends.
.to()
tl.to(".box", {
x: 200,
duration: 1
});
.from()
tl.from(".box", {
opacity: 0,
y: 100,
duration: 1
});
.fromTo()
tl.fromTo(
".box",
{ scale: 0 },
{ scale: 1, duration: 1 }
);
.set()
tl.set(".box", {
opacity: 0
});
Instant property change.
Labels are named positions inside a timeline.
Instead of using time values, you can use meaningful names.
tl.addLabel("start");
or
tl.addLabel("intro", 2);
const tl = gsap.timeline();
tl.addLabel("begin")
.to(".box1", {
x: 200,
duration: 1
})
.addLabel("middle")
.to(".box2", {
x: 200,
duration: 1
})
.addLabel("end");
Timeline:
begin ---- box1 ---- middle ---- box2 ---- end
Position parameters control WHEN animations start.
This is one of the most important Timeline concepts.
tl.to(".box1", {
x: 200,
duration: 1
});
tl.to(".box2", {
x: 200,
duration: 1
});
Result:
Box1 ----> Box2 ---->
Sequential.
tl.to(".box1", {
x: 200
}, 2);
Starts at 2 seconds.
"+=1"
tl.to(".box2", {
x: 200
}, "+=1");
Starts 1 second after previous animation ends.
Timeline:
Box1 ---->
(1s gap)
Box2 ---->
"-=0.5"
tl.to(".box2", {
x: 200
}, "-=0.5");
Starts 0.5s before previous animation ends.
Timeline:
Box1 -------->
Box2 -------->
Overlap created.
tl.to(".box1", {
x: 200
})
.to(".box2", {
y: 200
}, "<");
"<" means start at same time as previous animation.
Timeline:
Box1 --------> Box2 -------->
Both together.
tl.to(".box1", {
x: 200
})
.to(".box2", {
y: 200
}, "<0.5");
Starts 0.5 seconds after previous starts.
tl.to(".box1", {
duration: 2
})
.to(".box2", {
duration: 1
}, ">");
">" means start after previous animation ends.
Timelines can be controlled like videos.
tl.play();
Starts timeline.
tl.pause();
Stops at current position.
tl.resume();
Continues from pause point.
tl.restart();
Starts from beginning.
tl.reverse();
Plays backwards.
Jump to specific position.
tl.seek(2);
Move to 2 seconds.
tl.seek("intro");
tl.progress(0.5);
Moves timeline to 50%.
Controls playback speed.
tl.timeScale(2);
2x speed.
tl.timeScale(0.5);
Half speed.
<button id="play">Play</button>
<button id="pause">Pause</button>
<button id="reverse">Reverse</button>
document
.getElementById("play")
.addEventListener("click", () => tl.play());
document
.getElementById("pause")
.addEventListener("click", () => tl.pause());
document
.getElementById("reverse")
.addEventListener("click", () => tl.reverse());
ScrollTrigger is a GSAP plugin that lets you animate elements based on the user's scroll position.
Normally animations run automatically:
gsap.to(".box", {
x: 300,
duration: 2
});
With ScrollTrigger, animations start when the user scrolls to a specific point.
gsap.to(".box", {
x: 300,
scrollTrigger: ".box"
});
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js"></script>
gsap.registerPlugin(ScrollTrigger);
The trigger is the element that starts the animation.
gsap.to(".box", {
x: 300,
scrollTrigger: {
trigger: ".box"
}
});
User Scrolls
↓
Trigger Reaches Viewport
↓
Animation Starts
<section class="section">
<div class="box"></div>
</section>
gsap.to(".box", {
rotation: 360,
scrollTrigger: {
trigger: ".section"
}
});
Animation starts when .section enters viewport.
Control exactly when animation starts and stops.
start: "top center"
Meaning:
Element Top
↓
Viewport Center
Animation starts when element's top reaches viewport center.
start: "elementPosition viewportPosition"
scrollTrigger: {
trigger: ".box",
start: "top 80%"
}
Start when box reaches 80% of viewport.
end: "bottom center"
Animation ends when:
Element Bottom
↓
Viewport Center
scrollTrigger: {
trigger: ".box",
start: "top center",
end: "bottom center"
}
Scrub links animation progress directly to scrolling.
Without scrub:
gsap.to(".box", {
x: 500,
scrollTrigger: {
trigger: ".box"
}
});
Animation plays once triggered.
With scrub:
gsap.to(".box", {
x: 500,
scrollTrigger: {
trigger: ".box",
scrub: true
}
});
Animation follows scrollbar.
Scroll 20% → Animation 20% Scroll 50% → Animation 50% Scroll 100% → Animation 100%
scrub: 2
Meaning:
Animation catches up smoothly over 2 seconds.
scrollTrigger: {
trigger: ".box",
scrub: 2
}
Create horizontal scrolling sections.
<div class="container"> <section class="panel red"></section> <section class="panel blue"></section> <section class="panel green"></section> </div>
.container {
display: flex;
width: 300vw;
}
.panel {
width: 100vw;
height: 100vh;
}
let sections =
gsap.utils.toArray(".panel");
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none",
scrollTrigger: {
trigger: ".container",
pin: true,
scrub: 1,
end: "+=3000"
}
});
Scroll Down Panel 1 → Panel 2 → Panel 3
Instead of vertical movement.
let sections =
gsap.utils.toArray(".panel");
Converts all panels into an array.
[
redPanel,
bluePanel,
greenPanel
]
gsap.to(sections, {
Animate all panels.
xPercent: -100 * (sections.length - 1),
Let's calculate.
We have:
sections.length = 3
Therefore:
-100 * (3 - 1) = -200
Result:
xPercent: -200
Initially:
[Red][Blue][Green] ^ Visible
After moving:
[Red][Blue][Green]
^
Visible
Need to move:
100% for Blue 100% for Green
Total:
200%
Hence:
xPercent: -200
Negative means move left.
ease: "none"
No acceleration.
Required for smooth scroll syncing.
scrollTrigger: {
Creates scroll-based animation.
trigger: ".container",
Animation begins when container reaches viewport.
pin: true,
Pins container.
Normal scrolling:
Scroll ↓ Section passes away
Pinned:
Scroll ↓ Section stays fixed
This allows horizontal movement while page is still scrolling vertically.
scrub: 1,
Links animation to scrollbar.
Without scrub:
Trigger ↓ Animation plays automatically
With scrub:
Scroll 20% → Animation 20% Scroll 50% → Animation 50% Scroll 80% → Animation 80%
Perfect synchronization.
end: "+=3000"
Meaning:
Continue animation for 3000px of scrolling
Without enough scroll distance:
Scroll ↓ Animation ends too fast
With:
end: "+=3000"
User gets enough space to see:
Red → Blue → Green
Page Scrolls
↓
Container Reaches Screen
↓
Pin Activated
↓
Vertical Scroll Continues
↓
Panels Move Horizontally
↓
Red → Blue → Green
↓
End Reached
↓
Pin Released
Parallax creates depth by moving elements at different speeds.
Background moves slower.
gsap.to(".bg", {
y: -200,
scrollTrigger: {
trigger: ".section",
scrub: true
}
});
gsap.to(".content", {
y: -50,
scrollTrigger: {
trigger: ".section",
scrub: true
}
});
Background ← Slow Foreground ← Fast
Creates 3D depth illusion.
gsap.to(".image", {
yPercent: -20,
scrollTrigger: {
trigger: ".image",
scrub: true
}
});