GSAP Ease Gallery — Free Easing Comparison Snippet
GSAP Ease Gallery · Visualizers · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="geg-wrap">
<div class="geg-head">
<h3>Pick your motion personality</h3>
<button class="geg-play" id="gegPlay">▶ Race all eases</button>
</div>
<div class="geg-lanes" id="gegLanes"></div>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0b0d16;color:#fff;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}
.geg-wrap{width:min(640px,94vw);display:flex;flex-direction:column;gap:16px}
.geg-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.geg-head h3{font-size:clamp(17px,2.8vw,22px);font-weight:800;letter-spacing:-.01em}
.geg-play{padding:10px 20px;border-radius:12px;border:0;background:linear-gradient(120deg,#6366f1,#0ea5e9);color:#fff;font:700 13.5px system-ui;cursor:pointer}
.geg-play:hover{opacity:.9}
.geg-lanes{display:flex;flex-direction:column;gap:9px}
.geg-lane{position:relative;height:44px;border-radius:12px;background:#10152a;border:1px solid rgba(255,255,255,.09);overflow:hidden}
.geg-lane:hover{border-color:rgba(129,140,248,.4)}
.geg-name{position:absolute;left:14px;top:50%;transform:translateY(-50%);font:600 12px ui-monospace,monospace;color:#8a90a8;pointer-events:none}
.geg-ball{position:absolute;left:8px;top:50%;margin-top:-11px;width:22px;height:22px;border-radius:50%;background:radial-gradient(circle at 32% 28%,color-mix(in srgb,var(--bc) 60%,#fff),var(--bc));box-shadow:0 4px 12px rgba(0,0,0,.4);will-change:transform}// EasePack (loaded via CDN) adds rough(), slow(), and expoScale()
// to the core ease families — no registration needed as a script tag.
var EASES = [
{ name: 'power2.out', ease: 'power2.out', color: '#818cf8' },
{ name: 'power4.inOut', ease: 'power4.inOut', color: '#6366f1' },
{ name: 'back.out(1.7)', ease: 'back.out(1.7)', color: '#22d3ee' },
{ name: 'elastic.out(1, 0.3)', ease: 'elastic.out(1, 0.3)', color: '#f472b6' },
{ name: 'bounce.out', ease: 'bounce.out', color: '#fbbf24' },
{ name: 'circ.inOut', ease: 'circ.inOut', color: '#34d399' },
{ name: 'steps(8)', ease: 'steps(8)', color: '#fb7185' },
{ name: 'rough(...)', ease: 'rough({ strength: 2, points: 24, taper: "out", randomize: true })', color: '#c084fc' },
{ name: 'slow(0.7, 0.7)', ease: 'slow(0.7, 0.7, false)', color: '#4ade80' }
];
var lanes = document.getElementById('gegLanes');
EASES.forEach(function (item, i) {
var lane = document.createElement('div');
lane.className = 'geg-lane';
lane.innerHTML = '<span class="geg-name">' + item.name + '</span>' +
'<div class="geg-ball" style="--bc:' + item.color + '"></div>';
// Clicking a single lane replays just that ease.
lane.addEventListener('click', function () { run(lane, item.ease); });
lanes.appendChild(lane);
});
function run(lane, ease) {
var ball = lane.querySelector('.geg-ball');
var dist = lane.clientWidth - 38;
gsap.killTweensOf(ball);
gsap.fromTo(ball, { x: 0 }, { x: dist, duration: 1.8, ease: ease });
}
function runAll() {
document.querySelectorAll('.geg-lane').forEach(function (lane, i) {
run(lane, EASES[i].ease);
});
}
document.getElementById('gegPlay').addEventListener('click', runAll);
runAll();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 Ease Gallery — See Every Easing Personality Side by Side

Easing is the most consequential decision in any animation — the same 1.8-second, same-distance movement reads as confident, playful, broken, or robotic purely from its curve. But eases are impossible to choose from documentation graphs. This gallery makes them physical: nine balls race identical lanes over identical durations, differing *only* in ease — the core families plus EasePack's exotic rough() and slow() — with a race-all button and per-lane replays.
Identical everything except the curve
Every lane's tween is fromTo(ball, { x: 0 }, { x: dist, duration: 1.8, ease }). Distance is measured per lane (clientWidth − 38) so the race is fair at any container width. Because duration and distance are constant, all differences you see — the overshoot, the rubbery settle, the stutter — are pure easing. That controlled-experiment framing is what makes the gallery useful for *choosing*: you're comparing personalities, not implementations.
The core cast, briefly
power2.out is the workhorse UI ease (fast start, gentle landing); power4.inOut is its dramatic cousin with a violent middle. back.out(1.7) overshoots the target and returns — the parenthesized config is amplitude, tunable inline. elastic.out(1, 0.3) oscillates around the target like a plucked band (amplitude, period). bounce.out hits the end like a floor. circ.inOut is quarter-circle geometry — silkier than power curves at the extremes. steps(8) quantizes motion into eight discrete jumps, the sprite-sheet and mechanical-counter ease.
EasePack's exotics are the reason this file exists
rough({ strength: 2, points: 24, taper: 'out', randomize: true }) perturbs an underlying ease with jitter — the flickering-candle, damaged-machine, glitch ease; taper: 'out' calms it near the finish. slow(0.7, 0.7, false) rushes in, moves *almost not at all* through a long middle plateau, then rushes out — the slow-motion-highlight ease used for text flybys (its third parameter can even yoyo the value back). Both ship in EasePack, loaded as one extra CDN script and available by string name with zero registration.
Config-in-the-string is the underrated feature
back.out(1.7), elastic.out(1, 0.3), steps(8), rough({...}) — GSAP parses ease configuration from the string itself, so an ease choice (with tuning) is *data*, not code. This gallery exploits that: the entire lineup is an array of strings, and your production code can store ease choices in config, CMS fields, or design tokens the same way.
killTweensOf keeps replays honest
Clicking a lane mid-race kills that ball's tween and restarts from zero — the standard interrupt-safety idiom — so you can spam-compare two lanes rhythmically, which is genuinely the fastest way to feel the difference between, say, back and elastic settles.
What's deliberately absent
No curve graphs: graphs describe eases the way sheet music describes a song. Motion is the medium; judge eases by watching the balls. For designing *custom* curves, GSAP's CustomEase draws them as SVG paths — with CustomBounce and CustomWiggle as its generators.
Customizing it
Add lanes to the EASES array (name, ease string, color), race y-axis drops instead, or wire lane clicks to copy the ease string. Related: bounce physics in custom bounce ball, oscillation in custom wiggle icons, curve editing in bezier curve editor, and CSS-side timing in css animation generator.
Step by step
How to Use
- 1Add the GSAP CDNsInclude gsap and EasePack from the CDN panel.
- 2Paste HTML, CSS, and JSNine lanes build from the EASES array and race once.
- 3Click Race allAll balls launch together — same time, same distance.
- 4Replay one laneClick it; killTweensOf restarts that ball cleanly.
- 5Study rough and slowEasePack's exotics jitter and plateau mid-run.
- 6Add your candidatesOne array entry per ease string you're considering.
Real-world uses
Common Use Cases
rough() produces flicker like glitch text, and how slow() creates dramatic plateaus for flybys.steps() for counters like a flip clock, and compare with the custom bounce ball for squash and stretch.Got questions?
Frequently Asked Questions
Graphs describe position-over-time abstractly; motion is judged by the eye. With duration and distance held constant across lanes, every visible difference — overshoot, oscillation, stutter, plateau — is pure easing personality. Watching back.out and elastic.out settle side by side communicates their difference faster than any pair of plots.
Inline configuration parsed from the string: back's parameter is overshoot amplitude (1.7 is the classic default; 3 is cartoonish), elastic.out(1, 0.3) is amplitude and period (smaller period = faster oscillation), steps(8) is the jump count. Because config lives in the string, tuned eases are storable data — in tokens, CMS fields, or props.
Three specialty eases: rough(), which perturbs a base ease with configurable jitter (strength, point count, tapering) for flicker and glitch motion; slow(), which rushes in, holds a long near-still plateau, and rushes out — with an optional yoyo for flyby effects; and expoScale(), which linearizes perceived zoom rates between scale factors. One extra script, string-name access.
Whenever motion should be discrete: sprite-sheet frame playback, mechanical counters and flip displays, typewriter-adjacent movement, or retro/8-bit aesthetics where smoothness would break the fiction. steps(8) quantizes the tween into eight equal jumps — pair it with tabular numerals or frames sized to the step count.
run() begins with gsap.killTweensOf(ball) and a fromTo that re-asserts x: 0, so a fresh race replaces the old tween rather than stacking on it. Interrupt-safety matters double in a comparison tool — rhythmically replaying two lanes back-to-back is the fastest way to feel a difference, and that habit must not corrupt state.
Render lanes from the EASES array (map / v-for / *ngFor) with per-lane refs, fire run() from click handlers, and trigger the initial race in a mount effect — useEffect, onMounted, or ngAfterViewInit — with killTweensOf cleanup on unmount. Measure lane width at run time (as here) so responsive Tailwind layouts keep races fair; the ease strings themselves are framework-agnostic data.