Skip to main content

3.10 release

· 5 min read
Cassie Evans
Jack Doyle

Introducing ScrollSmoother.🥳

A shiny new plugin, exclusively for Club GSAP members!

ScrollSmoother makes it simple to add a buttery smooth vertical scrolling effect to your ScrollTrigger pages. Under the hood, ScrollSmoother leverages native scrolling which allows it to sidestep many of the accessibility annoyances that plague smooth-scrolling sites. No fake scrollbars, and no messing with pointer or touch functionality.


content: "#smooth-content",
wrapper: "#smooth-wrapper",
smooth: 1, // how long (in seconds) it takes to "catch up" to the native scroll position
effects: true, // looks for data-speed and data-lag attributes on elements
normalizeScroll: true, // prevents address bar from showing/hiding on most devices, solves various other browser inconsistencies
ignoreMobileResize: true // skips ScrollTrigger.refresh() on mobile resizes from address bar showing/hiding


ScrollSmoother will integrate seamlessly with all your scroll-triggered animations. but we've also added some bonus ScrollSmoother effects.

  • speed -Great for parallax effects! It adjusts the speed at which an element moves vertically while scrolling through the viewport. A speed of 1 is normal speed, 2 is double speed, etc.
  • lag -Add some lag* to gently flow elements behind the scroll before they ease back to their natural scroll position.

* no seriously, trust us. It's the good kind of lag.

<div data-speed="0.5"></div> <!-- half-speed of scroll -->
<div data-speed="2"></div> <!-- double-speed of scroll -->
<div data-speed="1"></div> <!-- normal speed of scroll -->

<div data-lag="0.5"></div> <!-- takes 0.5 seconds to "catch up" -->
<div data-lag="0.8"></div> <!-- takes 0.8 seconds to "catch up" -->
Video Explainer

Read the docs for all the juicy details, or pull up a seat and watch this short explainer video.


The brand new 3.5kb Observer plugin offers a super-flexible, unified way to sense meaningful events across all (touch/mouse/pointer) devices without wrestling with all the implementation details. Perhaps you want to respond to "scroll-like" user behavior which could be a mouse wheel spin, finger swipe on a touch device, a scrollbar drag, or a pointer press & drag...and of course you need directional data and velocity. No problem! Tell Observer which event types to watch (wheel, touch, pointer, and/or scroll) and it will collect delta values over the course of each requestAnimationFrame tick (debounced for performance by default) and automatically determine the biggest delta and then trigger the appropriate callback(s) like onUp, onDown, onDrag, etc.

Look how easy it is to trigger next()/previous() functions based on when the user swipes up/down or uses their mouse wheel:

target: window, // can be any element (selector text is fine)
type: "wheel,touch", // comma-delimited list of what to listen for ("wheel,touch,scroll,pointer")
onUp: () => previous(),
onDown: () => next(),


Notice there's no actual scrolling in the demo below but you can use your mouse wheel (or swipe on touch devices) to initiate movement so it "feels" like a scroll:


Since ScrollTrigger leverages Observer internally for normalizeScroll(), we exposed it via ScrollTrigger.observe() so you don't have to load an extra file if you're already using ScrollTrigger.

Video Walkthorugh

Excited? Why don't you observe this video or check out the docs (see what we did there?).


If you find yourself calling many times on the same numeric property of the same target, like in a "mousemove" event, you can boost performance by creating a quickTo() function instead. Think of a quickTo() like an optimized function tied to one particular numeric property, where it directly pipes a new number to it.


let xTo = gsap.quickTo("#id", "x", {duration: 0.4, ease: "power3"}),
yTo = gsap.quickTo("#id", "y", {duration: 0.4, ease: "power3"});

document.querySelector("#container").addEventListener("mousemove", e => {

Mouse Follower Demo


ScrollTrigger.normalizeScroll() and ignoreMobileResize

Have you ever run into these problems?:

  • Address bar on mobile browsers shows/hides and resizes the viewport, causing jumps
  • When scrolling fast, a pinned element seems to shoot past for a brief moment and then jump to the correct pinned position (multi-thread synchronization issues)
  • iOS jitter
  • Overscroll behavior that seems impossible to prevent on iOS
  • Inconsistent momentum scrolling across devices

The powerful new normalizeScroll() functionality intercepts native browser scroll behavior and handles it on the JavaScript thread instead which solves the problems mentioned above on most devices (iOS Phones in portrait orientation still show/hide the address bar). To enable it, simply:


To prevent ScrollTrigger.refresh() from running (and recalculating start/end positions) when a mobile browser shows/hides its address bar, you can now do:

ScrollTrigger.config({ ignoreMobileResize: true });

So easy!

Read more in the docs.

New "*=" and "/=" relative prefixes

You've always been able to add or subtract from the current value, like:".box", {
x: "+=100", // 100 more than the current value
y: "-=100", // 100 less than the current value

...and now you can multiply or divide accordingly:".box", {
x: "*=2", // double the current value
y: "/=2", // halve the current value

And more...


GSAP 3.10 also delivers various bug fixes, so we'd highly recommend installing the latest version today. There are many ways to get GSAP - see the Installation page for all the options (download, NPM, zip, Github, etc.).


Happy tweening!