GSAP Draggable Inertia — Free Momentum Drag & Snap Snippet
GSAP Draggable Inertia · Animations · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="di-wrap">
<div class="di-stage" id="diStage">
<div class="di-token" id="diToken">Drag me</div>
</div>
<p class="di-hint">Flick the token — it keeps moving with momentum, then eases to a stop inside the bounds.</p>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0a0c14;color:#fff;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}
.di-wrap{display:flex;flex-direction:column;align-items:center;gap:16px;width:min(560px,94vw)}
.di-stage{position:relative;width:100%;aspect-ratio:16/10;border-radius:20px;background:radial-gradient(120% 100% at 30% 0%,#1a1440,#0a0c14 70%);border:1px solid rgba(255,255,255,.1);overflow:hidden}
.di-stage::before{content:'';position:absolute;inset:14px;border:1px dashed rgba(129,140,248,.25);border-radius:14px;pointer-events:none}
.di-token{position:absolute;top:20px;left:20px;width:96px;height:96px;border-radius:20px;background:linear-gradient(160deg,#818cf8,#4c1d95);display:flex;align-items:center;justify-content:center;font:700 13px system-ui;color:#fff;cursor:grab;box-shadow:0 12px 30px rgba(76,29,149,.5);user-select:none;touch-action:none}
.di-token:active{cursor:grabbing}
.di-hint{color:#7d84a0;font-size:13px;text-align:center;max-width:440px}gsap.registerPlugin(Draggable, InertiaPlugin);
// The token is draggable within the stage bounds and keeps moving on release,
// decelerating naturally instead of stopping dead where the pointer let go.
Draggable.create('#diToken', {
type: 'x,y',
bounds: '#diStage',
edgeResistance: 0.65,
inertia: true,
onDragStart: function () {
gsap.to(this.target, { scale: 1.06, boxShadow: '0 18px 40px rgba(76,29,149,.65)', duration: 0.2 });
},
onDragEnd: function () {
gsap.to(this.target, { scale: 1, boxShadow: '0 12px 30px rgba(76,29,149,.5)', duration: 0.3 });
}
});Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
GSAP Draggable Inertia — Flick-and-Coast Dragging With Bounded Momentum

The GSAP Draggable Inertia snippet is the difference between a drag interaction that feels mechanical and one that feels physical — release the token mid-flick and it keeps traveling in the direction and speed you threw it, decelerating naturally instead of stopping dead the instant your pointer lifts. It's built with GSAP's Draggable utility plus the InertiaPlugin, loaded from a CDN, and a single bounded stage element.
Draggable does the pointer work
Draggable.create('#diToken', { type: 'x,y', bounds: '#diStage', ... }) hands off all the pointer and touch tracking — mouse, touch, and pen input are normalized into one API, so the token drags smoothly across desktop and mobile without you writing a single pointermove listener. type: 'x,y' lets it move freely in both axes.
Inertia is the momentum
Setting inertia: true (with InertiaPlugin registered) is what turns a plain drag into a throw. When you release the token while it's still moving, GSAP calculates its velocity at release and continues the motion, decaying it smoothly to zero rather than an abrupt stop — the same feel as flicking a card across a table.
Bounds keep it honest
bounds: '#diStage' constrains both the drag and the inertial coast to the stage element, and edgeResistance: 0.65 adds resistance as the token nears the edge during an active drag, so it feels like it's pushing against a soft wall rather than hitting a hard stop. The coast phase respects the same bounds, so a hard flick toward the edge settles just inside it instead of overshooting.
Feedback on grab and release
onDragStart and onDragEnd callbacks scale the token up slightly and deepen its shadow while held, then relax it back — a small tactile cue that reinforces the drag without interfering with Draggable's own transform handling.
Customizing it
Swap type: 'x,y' for 'x' or 'y' to constrain to one axis, tune edgeResistance for a softer or firmer edge feel, or add onThrowComplete to react once the coast finishes. Pair it with a drag sort list or drag throw notes for a board of independently-flickable items.
Step by step
How to Use
- 1Add the GSAP CDNsInclude gsap, Draggable, and InertiaPlugin from the CDN panel.
- 2Paste HTML, CSS, and JSA bounded stage with one draggable token renders.
- 3Drag the tokenIt follows the pointer and resists near the edges.
- 4Flick and releaseIt keeps coasting with momentum, then eases to a stop.
- 5Try a slow drag and releaseWith little velocity, it stops close to where you let go.
- 6Adjust bounds or resistanceChange #diStage or edgeResistance to retune the feel.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Plain Draggable stops the element exactly where the pointer releases it. InertiaPlugin calculates the element's velocity at the moment of release and continues its motion, decelerating it smoothly to zero — that's what produces the throw-and-coast feel instead of an abrupt stop.
InertiaPlugin is a Club GreenSock bonus plugin. Recent versions of the public gsap npm/CDN package have bundled it for no-signup use in many contexts, but licensing terms can change and commercial use may require a Club GreenSock membership depending on your use case — check GreenSock's current licensing page before shipping this in a commercial product.
Yes. bounds: '#diStage' constrains both the active drag and the inertial coast, so a hard flick toward an edge decelerates and settles just inside the boundary rather than flying out of the stage.
Yes. Draggable normalizes mouse, touch, and pen input into one API, and touch-action: none on the token stops the browser from interpreting the drag as a page scroll, so flicks register cleanly on mobile.
Render the stage and token, then in a mount effect register Draggable and InertiaPlugin and call Draggable.create scoped to the container. Store the returned Draggable instance and call .kill() in the cleanup function to avoid duplicate instances on re-render or unmount.