Scroll velocity
Scroll velocity is the speed and direction the user is scrolling, measured in pixels per second, which ScrollTrigger exposes through `self.getVelocity()` inside its callbacks. Reading it lets motion react to how fast someone scrolls instead of how far they have scrolled, so an effect can spike on a hard flick and relax the moment the scroll stops.
Updated June 30, 2026
How scroll velocity works
scrub ties an animation to scroll POSITION. Scroll velocity is the other axis: it reads how fast the scroll is moving right now. ScrollTrigger gives you the number through self.getVelocity() on the instance, available inside onUpdate and the other callbacks. The value is signed, so a positive number means scrolling down and a negative number means scrolling up.
The raw number is large (hundreds to thousands on a fast flick), so the pattern is always the same three steps: scale it down into the range your property wants, clamp it so a violent scroll can't blow past your bounds, then write it cheaply with a quickSetter. To make it ease back to rest when scrolling stops, tween a proxy value back to 0 and apply that proxy every frame.
Why the decay tween matters
getVelocity() is non-zero only while the scroll is actually moving. If you write the property directly from the velocity, the effect snaps back to rest the instant scrolling stops, which reads as a hard pop. Tweening a proxy back to 0 (and applying it via onUpdate) is what makes the effect ease out smoothly after a flick.Use scroll velocity for
- Kinetic type that gains weight or width on fast scroll and settles when the reader stops
- Directional lean, where text or an element tilts into the scroll direction (sign of the velocity drives it)
- Clip or mask reveals that get more aggressive the faster someone scrolls
- Image trails or stagger effects whose intensity scales with scroll speed
Why this reads as crafted
Use something else when
- You want progress through a range, not speed - use
scruband bind the tween to scroll position instead - The effect is purely enter or leave, with no speed component - use
toggleActions - You need input velocity without ScrollTrigger (wheel, touch, drag) - use the
Observerplugin, which reportsvelocityXandvelocityYdirectly - You're driving a high-frequency property like cursor follow - reach for
gsap.quickTo, which has its own velocity-aware lag built in
Used in these Annnimate components
Both of Annnimate's velocity components read self.getVelocity() on every onUpdate and write the result through font-variation-settings or a clip-path, then ease back to rest when the scroll stops:
- Velocity Type maps scroll speed to variable-font axes (weight, width, slant) so a headline thickens and tightens on a fast scroll, and in directional mode leans into the scroll direction before settling back to its resting axes
- Velocity Clip drives a clip-path from scroll speed, so elements clip and unclip harder the faster you scroll, then relax when momentum dies
See it running in production
Common questions
- How do I get scroll velocity in GSAP?
- Read
self.getVelocity()inside a ScrollTrigger callback. Create a trigger (or a standaloneScrollTrigger.create({ onUpdate })), and inonUpdatethe instance is passed in, soself.getVelocity()returns the current scroll speed in pixels per second. There's no separate plugin to register beyond ScrollTrigger itself. - What units does getVelocity return?
- Pixels per second, signed by direction. Scrolling down gives a positive number, scrolling up gives a negative one. The magnitude is large on a fast scroll (often hundreds or thousands), so divide it down before mapping it onto a property like skew, weight, or a clip value, and clamp the result so a hard flick can't exceed your bounds.
- Scroll velocity vs scrub - what's the difference?
scrubbinds an animation's progress to scroll POSITION, so the tween plays forward and backward as you scroll through a range and is identical at the same scroll point every time. Scroll velocity reads SPEED, so the effect depends on how fast you're moving and decays to rest when you stop, regardless of where you are on the page. Use scrub for position-locked reveals and velocity for speed-reactive feedback.- Why does my velocity effect snap back instead of easing out?
- Because you're writing the property straight from
getVelocity(), which drops to 0 the instant the scroll stops. The fix is to tween a proxy value back to 0 over ~0.6-0.8s and apply that proxy on the tween'sonUpdate, only overriding it when a new frame's velocity spike is larger than the proxy's current value. That decay tween is what gives the smooth settle. - Can I read scroll velocity without ScrollTrigger?
- Yes, the
Observerplugin reportsvelocityXandvelocityYfor wheel, touch, and pointer input without needing a scroll position at all. Use Observer when the motion is driven by raw input speed (a custom scroller, a drag surface) and ScrollTrigger'sgetVelocity()when you're already tracking a scroll range and want the speed alongside the position.
