Bring Your UI to Life with ScrollTrigger Animations in GSAP

Scroll-based animations can transform a static website into an engaging, interactive experience. While CSS animations are useful for basic effects, creating sophisticated scroll-driven animations requires a more powerful tool. GSAP’s ScrollTrigger plugin is exactly that tool—letting you create polished, performant animations triggered by scroll position.
In this guide, you’ll learn how to implement ScrollTrigger animations that respond naturally to user scrolling, with practical examples you can use in your projects today.
Key Takeaways
- ScrollTrigger connects animations to scroll position for interactive experiences
- Use
scrub
to tie animation progress directly to scroll position - Pin elements to create advanced scroll-based effects
- Configure
start
andend
to precisely control when animations trigger - Use
markers
during development to visualize trigger points
What is ScrollTrigger and Why Use It?
ScrollTrigger is a GSAP plugin that connects animations to the scroll position. Unlike basic “on-scroll” libraries that simply trigger animations when elements enter the viewport, ScrollTrigger offers precise control over:
- When animations start and end based on scroll position
- How animations progress as users scroll (scrubbing)
- Pinning elements while users scroll past them
- Creating complex scroll-based interactions
The result? Animations that feel connected to user scrolling rather than just playing when triggered.
Getting Started with ScrollTrigger
First, let’s set up the basics:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ScrollTrigger Demo</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.spacer {
height: 100vh;
}
.box {
width: 200px;
height: 200px;
background-color: #3498db;
border-radius: 8px;
}
</style>
</head>
<body>
<div class="spacer"></div>
<section>
<div class="box"></div>
</section>
<div class="spacer"></div>
<!-- GSAP and ScrollTrigger -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
<script>
// Register the plugin
gsap.registerPlugin(ScrollTrigger);
// Your animations will go here
</script>
</body>
</html>
Basic ScrollTrigger Animation
Let’s start with a simple animation that triggers when an element enters the viewport:
gsap.to(".box", {
scrollTrigger: ".box", // Element that triggers the animation
x: 300, // Move 300px to the right
rotation: 360, // Rotate 360 degrees
duration: 1.5, // Animation duration
ease: "power2.out" // Easing function
});
This code moves and rotates the box when it enters the viewport. But this is just scratching the surface.
Understanding ScrollTrigger Configuration
To unlock ScrollTrigger’s full potential, we need to understand its configuration options:
gsap.to(".box", {
scrollTrigger: {
trigger: ".box", // Element that triggers the animation
start: "top center", // Start when top of box hits center of viewport
end: "bottom center", // End when bottom of box hits center of viewport
toggleActions: "play pause reverse reset", // Actions on enter, leave, enter back, leave back
markers: true, // Shows markers for debugging (remove in production)
},
x: 300,
rotation: 360,
duration: 2
});
The start
and end
properties define when the animation activates and deactivates. The format is "[trigger element position] [viewport position]"
.
toggleActions
controls how the animation behaves at four key moments:
- When entering the trigger area
- When leaving the trigger area
- When entering the trigger area again while scrolling up
- When leaving the trigger area while scrolling up
Options include: play
, pause
, resume
, reverse
, restart
, reset
, complete
, and none
.
Creating Scroll-Driven Animations with Scrub
The real magic happens with the scrub
property, which ties animation progress directly to scroll position:
gsap.to(".box", {
scrollTrigger: {
trigger: ".box",
start: "top center",
end: "bottom center",
scrub: true, // Links animation progress to scroll position
markers: true
},
x: 300,
rotation: 360,
backgroundColor: "#e74c3c"
});
With scrub: true
, the animation progresses as the user scrolls, and even reverses when scrolling back up. For smoother animation, use a number value like scrub: 0.5
to add a slight delay.
Pinning Elements During Scroll
One of ScrollTrigger’s most powerful features is pinning elements in place while the user scrolls:
gsap.to(".box", {
scrollTrigger: {
trigger: ".box",
start: "center center",
end: "+=300", // End 300px after the start position
pin: true, // Pin the box in place during the animation
scrub: 1,
markers: true
},
x: 300,
rotation: 360,
scale: 1.5,
backgroundColor: "#9b59b6"
});
This pins the box in place while the animation plays, creating a parallax-like effect. The end: "+=300"
means the animation ends after scrolling 300 pixels beyond the start point.
Creating a Reveal Animation
Let’s create a practical reveal animation for text or images:
<div class="spacer"></div>
<section class="reveal-section">
<div class="reveal-container">
<h1 class="reveal-text">Scroll-Driven Animations</h1>
<p class="reveal-text">Create engaging user experiences with GSAP ScrollTrigger</p>
</div>
</section>
<div class="spacer"></div>
.reveal-section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.reveal-container {
max-width: 800px;
text-align: center;
overflow: hidden;
}
.reveal-text {
opacity: 0;
transform: translateY(50px);
}
// Reveal animation
gsap.utils.toArray('.reveal-text').forEach(text => {
gsap.to(text, {
scrollTrigger: {
trigger: text,
start: "top 80%", // Start when top of text is 80% from top of viewport
toggleActions: "play none none none"
},
y: 0,
opacity: 1,
duration: 1,
ease: "power2.out"
});
});
This creates a clean reveal effect as each text element enters the viewport.
Creating a Parallax Effect
Parallax effects add depth to your website. Here’s how to create one:
<div class="parallax-container">
<div class="parallax-bg"></div>
<div class="parallax-content">
<h1>Parallax Effect</h1>
</div>
</div>
.parallax-container {
height: 100vh;
position: relative;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.parallax-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 120%; /* Extra height for movement */
background-image: url('your-background-image.jpg');
background-size: cover;
background-position: center;
}
.parallax-content {
position: relative;
z-index: 1;
color: white;
text-align: center;
}
// Parallax effect
gsap.to(".parallax-bg", {
scrollTrigger: {
trigger: ".parallax-container",
start: "top bottom",
end: "bottom top",
scrub: true
},
y: -100, // Move background up by 100px as we scroll
ease: "none"
});
This creates a simple parallax effect where the background moves at a different rate than the foreground.
Horizontal Scrolling Section
Creating a horizontal scrolling section is another impressive effect:
<div class="spacer"></div>
<section class="horizontal-scroll">
<div class="horizontal-container">
<div class="panel">Panel 1</div>
<div class="panel">Panel 2</div>
<div class="panel">Panel 3</div>
<div class="panel">Panel 4</div>
</div>
</section>
<div class="spacer"></div>
.horizontal-scroll {
overflow: hidden;
height: 100vh;
}
.horizontal-container {
display: flex;
width: 400%; /* 100% * number of panels */
height: 100%;
}
.panel {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
}
.panel:nth-child(1) { background-color: #3498db; }
.panel:nth-child(2) { background-color: #2ecc71; }
.panel:nth-child(3) { background-color: #e74c3c; }
.panel:nth-child(4) { background-color: #9b59b6; }
// Horizontal scroll
gsap.to(".horizontal-container", {
scrollTrigger: {
trigger: ".horizontal-scroll",
start: "top top",
end: "+=3000", // Scroll distance
pin: true,
scrub: 1,
},
x: () => -(document.querySelector(".horizontal-container").offsetWidth - window.innerWidth),
ease: "none"
});
This creates a section that scrolls horizontally as the user scrolls vertically.
Performance Optimization Tips
ScrollTrigger animations can impact performance if not implemented carefully. Here are some tips:
- Use
will-change
sparingly: Only apply to elements that actually animate - Avoid animating layout properties: Stick to transform and opacity when possible
- Batch similar animations: Use
gsap.utils.toArray()
and loop through elements - Kill ScrollTriggers when not needed: Use
scrollTrigger.kill()
for single-page applications - Reduce marker usage: Remove
markers: true
in production
Troubleshooting Common Issues
Animation Starts Too Early/Late
If your animation triggers at unexpected times, check your start
and end
values. Use markers: true
to visualize trigger points.
Jerky Animations
For smoother animations, use scrub: 0.5
or higher instead of scrub: true
to add a slight delay.
Mobile Compatibility Issues
Mobile browsers handle scroll events differently. Test thoroughly on mobile devices and consider using ScrollTrigger.matchMedia()
to create different animations for different screen sizes.
Conclusion
With these techniques, you can create scroll animations that respond naturally to user interaction, enhancing your UI without overwhelming users. Start with simple effects and gradually incorporate more advanced techniques as you become comfortable with ScrollTrigger’s capabilities.
FAQs
Yes, but you need to properly set up and clean up ScrollTrigger instances when components mount/unmount.
ScrollTrigger is designed for GSAP, but you can use its callback functions to trigger other libraries.
Use percentages for positioning, and refresh ScrollTrigger on window resize with ScrollTrigger.refresh().
Basic functionality is free, but some advanced features require a GreenSock Club membership.
Use markers: true to visualize trigger points and console.log in callback functions to track progress.