Eases in GSAP: the shape of time, extended
An ease in GSAP is nothing more exotic than a plain JavaScript function dropped into one line of the interpolation core, and because it's a real function rather than four fixed numbers, GSAP can express overshoot, oscillation, and hand-authored curves that a CSS cubic-bezier structurally cannot.
Eases in GSAP: the shape of time, extended
The animation module's easing lesson established the core idea once, for CSS: a timing function is a pure function progress = f(t), and cubic-bezier() is one particular family of f — four control points defining a smooth curve that can dip below 0 or rise above 1 to produce anticipation or overshoot, but is still, underneath, a fixed cubic polynomial computed the same way every time. GSAP's eases are the same idea, generalized. Where CSS commits to "curves a cubic Bézier can express," GSAP commits to "any function you can write in JavaScript" — and that one difference is why GSAP has eases that CSS has no equivalent for.
Where the ease slots into the core
Go back to lesson 1's fifteen-line tween. The tick method computed:
const progress = Math.min(this.elapsed / this.duration, 1);
this.target[this.prop] = this.start + (this.end - this.start) * progress;Easing is one line inserted between those two, exactly the plug-in point lesson 1 predicted:
const progress = Math.min(this.elapsed / this.duration, 1); // linear, 0->1
const p = ease(progress); // reshaped, ~0->1
this.target[this.prop] = this.start + (this.end - this.start) * p;ease is a plain function: it takes normalized, linear progress in [0, 1] and returns a reshaped progress that's usually in [0, 1] but — just like a cubic-bezier's y-values — isn't required to stay there. Nothing else about the tween changes. The interpolation formula, the clamped elapsed-time-to-progress conversion, the write-back onto the target — all identical to lesson 1. ease: in a GSAP config is just telling the tween which function to call at that one line.
Why "just a function" is the whole point
A cubic-bezier(x1,y1,x2,y2) curve is defined once, by four numbers, and then evaluated — it can only ever be the shape those four numbers describe. A JavaScript function has no such restriction: it can branch, it can use trigonometry, it can carry parameters, it can hold internal state if you really wanted it to (GSAP's don't — they stay pure functions of progress, which is what keeps them scrubbable, exactly like lesson 1's progress = 0.4 seek). Because GSAP eases are just functions, the library ships families of them that would be difficult or outright impossible to express as four control points on a Bézier curve.
The built-in ease families
Each family below is really a description of what it does to velocity — the slope of progress against t — the same lens the animation-module lesson used to read cubic-bezier() control points:
none/"linear"—ease(progress) = progress, unchanged. Constant velocity, the mechanical-looking motion the easing lesson opened with.power1throughpower4(.in,.out,.inOut) — polynomial curves (progress,progress²,progress³,progress⁴, roughly), where the number is the steepness.power1is a gentle curve close toeasein CSS;power4is a much sharper acceleration or deceleration. These are GSAP's bread-and-butter defaults and the closest in spirit to a hand-tunedcubic-bezier()— smooth, monotonic (never overshoots), just with more graduated steepness options than CSS's three named keywords give you.back— overshoots past the endpoint, then settles back. Concretely,back.out(1.7)produces a curve whereease(progress)exceeds1before easing down to exactly1atprogress = 1— precisely the "output y greater than 1" overshoot the animation-module lesson showed a cubic-bezier control point can produce, exceptbackexposes it as a tunable parameter (that1.7) controlling how far past the end it overshoots, rather than something you reverse-engineer from control-point coordinates.elastic— oscillates, springing past the target and wobbling back and forth before settling, parameterized by amplitude (how far it swings) and period (how fast it wobbles). This is the family that cannot be a single cubic-bezier at all: a Bézier curve, being a cubic polynomial, changes direction (relative to its own slope) at most a couple of times over[0,1]— it has no way to encode several full oscillations. Producing "swing past, swing back, swing past a little less, settle" requires a genuinely different kind of math (GSAP'selasticis built from a sine wave with decaying amplitude, not a polynomial), which is exactly why it has to be delivered as a JavaScript function rather than four numbers — the same point the animation-module lesson made about spring physics being a live simulation rather than a fixed curve.bounce— mimics a ball bouncing to a stop: several decreasing hops, each one a smallpower-like arc. Also not expressible as one smooth Bézier, for the same reason aselastic— it isn't one continuous acceleration, it's several discontinuous ones stitched together.steps(n)— discrete jumps, exactly CSS'ssteps(): holds a value, then jumps,ntimes, no blending between. Useful for the same sprite-sheet-style cases the animation module covered — now available as an ease inside any GSAP tween rather than only a CSSanimation.expo,circ,sine— named for the actual math function shaping them (exponential, circular, sinusoidal). Each is smooth and monotonic likepower, just with a differently-shaped acceleration curve; useful whenpower1–power4's polynomial feel isn't quite the curve you want.
in / out / inOut is one operation applied to any ease
Every family above except none and steps exposes .in, .out, and .inOut variants (power2.in, power2.out, power2.inOut), and it's worth seeing these three as one general transformation rather than three separate curves to memorize per family:
.inis the ease as originally defined — slow start, since the underlying function (a power curve, an exponential, whatever) is flat nearprogress = 0..outis.in, reflected: run time backward through the same function and flip the result, so the motion that was slow-then-fast becomes fast-then-slow. Mechanically,easeOut(p) = 1 - easeIn(1 - p)..inOutis piecewise: the first half of the duration runs.in's shape compressed into[0, 0.5], the second half runs.out's shape compressed into[0.5, 1], stitched together at the midpoint — the same S-curve idea the animation lesson described forcubic-bezier'sease-in-out, just generalized to apply to any underlying ease family, not only a cubic polynomial.
That means learning power3.in's shape also tells you what power3.out and power3.inOut look like — they're the same underlying steepness, just mirrored or split. The transformation is orthogonal to which family you picked.
gsap.to(box, { x: 300, duration: 0.6, ease: "power2.out" }); // fast start, decelerate into place — no overshoot
gsap.to(card, { scale: 1, duration: 0.8, ease: "back.out(1.7)" }); // overshoots scale slightly, settles — the "1.7" is the overshoot amount
gsap.to(pin, { y: 0, duration: 1, ease: "elastic.out(1, 0.3)" }); // amplitude 1, period 0.3 — springs past 0, wobbles, settlesA short lead-in before you try this yourself: pick an ease below and watch its curve get drawn and a dot get animated along it, so back's overshoot and elastic's oscillation stop being words and become something you can see happen.
An ease is just a function of normalized progress: eased = f(t). Because it's plain JS rather than a fixed curve shape, GSAP can express eases a single CSS cubic-bezier can't — notice how back.out and elastic.out swing outside the 0..1 box (the curve dips below 0 or rises above 1), and bounce.out oscillates on the way in. A cubic-bezier is monotonic by construction; it can never overshoot or bounce like that.
CustomEase: the escape hatch
Sometimes none of the built-in shapes are the curve you actually want — a curve an animator drew by hand, or a company's specific "brand motion" signature. GSAP's CustomEase plugin lets you author an ease from an SVG-like path (control points you can literally draw), or by lifting the control points out of an existing cubic-bezier() you already like, and registers it under a name you can use as any other ease: string. Structurally, it's still the exact same plug-in point: CustomEase.create(...) produces a function, and that function gets called at the one line — const p = ease(progress) — same as power2.out or elastic.out(1, 0.3). It's not a special case bolted onto the ease system; it's proof that the ease system was "any function from progress to progress" all along, and the named families were just convenient, pre-written ones.
Easing is still orthogonal to duration
The animation module's lesson made this point for CSS and it survives unchanged here: duration says how much wall-clock time the tween takes; ease says what shape progress takes across that fixed window. Swap power1.out for elastic.out(1, 0.3) on the same tween and the duration — how long until the tween reports finished — doesn't change at all; only the path progress takes to get from 0 to 1 does. Halve the duration and the ease's shape is unaffected — you get the identical curve, just traversed twice as fast. Keeping these separate in your head is what makes ease: a knob you can turn without secretly breaking your timing, and it's exactly why a timeline (lesson 4) can freely reshape any child's ease without touching where that child sits on the parent's time axis.
Where this goes next
Everything in this lesson still assumed the tween is writing a plain number onto target[prop] — progress reshaped by ease, multiplied into a delta, added to a start. Real GSAP targets are DOM elements with transform, and transform is not one number, it's a matrix combining translation, rotation, scale, and skew, several of which interact in ways that aren't simply additive. The next lesson is about what actually happens between "GSAP computed an eased progress value" and "the box visibly moved."
Go deeper
- GSAP docs — Eases — The full list of ease families, their .in/.out/.inOut variants, and the exact parameters back and elastic accept.
- GSAP docs — CustomEase — How to author an arbitrary ease from a path or an existing cubic-bezier, the escape hatch this lesson ends on.
- GSAP docs — gsap.parseEase() — Confirms eases resolve to a plain function GSAP calls with a single progress argument — the exact plug-in point this lesson describes.
Check yourself
Answer out loud, as if an interviewer asked. If you hand-wave, reread that section.
- Write the two-line version of the interpolation core with easing inserted, and say exactly which line the ease function modifies.
- Name two GSAP ease families that are monotonic (never overshoot 1) and two that are not. What does 'overshoot' mean in terms of the ease function's output range?
- Why can elastic's oscillating behavior not be expressed as a single cubic-bezier(), in terms of what a cubic polynomial's curvature can and can't do?
- Explain .out as a transformation of .in. If you know power3.in's shape, what do you now know about power3.out without looking it up?
- What does back.out(1.7) parameterize, concretely, and what happens to the curve if you increase that number?
- In what sense is CustomEase not a special case, but proof of what the ease system already was?
- Restate why duration and ease are orthogonal, using the specific example of swapping power1.out for elastic.out on the same tween.