Observer
Observer is a GSAP plugin that normalizes wheel, touch, and pointer input into one set of direction and velocity callbacks, so you can react to a gesture without binding it to scroll position the way ScrollTrigger does. It's how you build swipe decks, scroll-jacked section steppers, and direction-aware UI on a single event model that behaves the same on a trackpad, a mouse wheel, and a phone.
Updated June 30, 2026
How Observer works
You call Observer.create() with a target element and a set of direction callbacks. Observer listens to the raw input events on that target, figures out which way the user moved, and fires onUp / onDown / onLeft / onRight once the movement passes the tolerance threshold. It does not move anything itself. You decide what each callback runs.
The callback receives the Observer instance, so you can read self.deltaX / self.deltaY (how far this move went), self.velocityX / self.velocityY (how fast), and self.isDragging. That velocity signal is the part native event handlers make you compute by hand.
type controls what it listens to
type defaults to "wheel,touch,pointer". Drop wheel if you only want touch and drag gestures, or set type: "touch,pointer" for a swipe-only carousel that ignores the scroll wheel. Set preventDefault: true only when you're fully taking over the input, since it stops the page from scrolling normally.Use it for
- Full-page section steppers where one wheel notch or swipe advances to the next panel
- Swipe-driven carousels and card decks that need to feel the same on touch and trackpad
- Detecting scroll direction (up vs down) to hide or show a header, without caring about exact scroll position
- Custom drag interactions where you want
deltaX/velocityXbut not the full weight of Draggable - Gesture handling on a nested scroll container or an element that isn't the window
Use something else when
- You need to tie animation progress to actual scroll position (parallax, scrubbed reveals, pinning), use
ScrollTriggerwithscrub - You're dragging an element around the screen with bounds and momentum, use
DraggableplusInertiaPlugin - You only need a single touch start/end and no velocity or normalization, native pointer events are lighter
- You want momentum scrolling of the page itself, that's
ScrollSmootheror Lenis, not Observer
Where direction signals show up in Annnimate components
Both of these solve the exact problem Observer is built for, reading the direction of a gesture and reacting to it. They reach it through different inputs, but Observer is the standalone plugin you'd grab to rebuild the direction signal from scratch or move it onto a non-window scroller.
- Hide Header hides on scroll-down and shows on scroll-up. It currently reads
self.directionoff a ScrollTrigger, which is the same up/down signal Observer'sonUp/onDowngive you. Observer is the lighter route when you want that signal without binding to scroll position, for example inside an app shell that scrolls its own panel instead of the window - Mega Menu plays a direction-aware reveal, animating each panel in from whichever side the cursor came from. That left/right intent is the pointer equivalent of Observer's
onLeft/onRightcallbacks
Free, and worth gating on reduced motion
prefers-reduced-motion adoption passed 50% of mobile sites in 2024, up from 34% in 2022 (HTTP Archive Web Almanac 2024). Use gsap.matchMedia() for that branch.See it running in production
Common questions
- What's the difference between Observer and ScrollTrigger?
- ScrollTrigger ties animation to scroll position, you give it a start and end on the page and it plays, scrubs, or pins within that range. Observer ignores position entirely. It just tells you the user moved up, down, left, or right with some velocity, and lets you decide what happens. Use ScrollTrigger when 'how far down the page am I' matters, use Observer when 'which way did they swipe' matters. ScrollTrigger actually uses Observer internally for its input normalization.
- How do I detect swipe direction with GSAP Observer?
- Set
type: "touch,pointer"and addonLeft/onRight(oronUp/onDown) callbacks toObserver.create(). Each fires once the movement passestolerancepixels in that direction. Readself.deltaXandself.velocityXinside the callback if you want the throw distance and speed, for example to decide whether a swipe was forceful enough to advance two cards instead of one. - Does Observer work on both touch and mouse wheel?
- Yes, that's the point of it. The
typeoption defaults to"wheel,touch,pointer", so the sameonUp/onDowncallbacks fire whether the user scrolled a wheel, swiped a touchscreen, or dragged with a pointer. You write the gesture logic once and it behaves consistently across devices instead of wiring upwheel,touchmove, andpointermoveseparately. - How do I stop or clean up an Observer?
Observer.create()returns the instance. Callobserver.kill()to remove its listeners, andobserver.disable()/observer.enable()to pause and resume without tearing it down. In React, kill it inside theuseGSAPcleanup return so it doesn't leak across re-renders or unmounts.- Is the Observer plugin paid or Club-only?
- It's free. Observer ships in the free GSAP core and has since before the licensing change, and as of GSAP 3.13 every plugin, including the former Club GSAP ones, is free for commercial use. You just
import { Observer } from "gsap/Observer"and register it.
revert() call cleans them all up at once.