easing-curves-timing-design
Easing curves and timing design
When to use this
- You already know which animation library/API you're using (see the other skills in this folder for syntax) and need to decide the actual numbers: duration in ms, which curve, how much overshoot.
- A reviewer or your own eye says an animation "feels off" or "feels AI-generated" and you need a principled way to diagnose why, rather than randomly nudging numbers.
- You're building or auditing a design system's motion tokens (a shared set of durations/curves reused across components) and need defensible defaults with rationale.
- You need to translate a motion reference (a competitor's app, a design file annotation, a named platform curve like "Material standard" or "iOS spring") into concrete cubic-bezier or spring values.
- Do NOT use this for how to implement a given curve in a specific library's API syntax; that is covered in each library-specific skill (
gsap-core-timelines,motion-one-waapi,framer-motion-*, etc). This skill is about the craft decision, not the code.
This skill is deliberately library-agnostic and is meant to be read alongside whichever execution-focused skill you are actually implementing in; the numbers and reasoning here transfer directly regardless of which library ends up running the animation.
Mental model
The rest of this document works through the actual mechanics (bezier anatomy, spring physics), the standard conventions (entrance/exit/move), and a concrete numeric/curve reference table, in that order, so you can either read it straight through once or jump to the reference table when you already know which category of motion you are tuning.
A cubic-bezier easing curve for animation timing is a 1-dimensional function mapping elapsed time (x-axis, 0 to 1) to progress (y-axis, 0 to 1), defined by two control points, cubic-bezier(x1, y1, x2, y2). The first control point shapes the beginning of the motion, the second shapes the end; critically, x1 and x2 are usually constrained close to their "natural" role (the curve must be a valid function of time, so x-values are typically kept between 0 and 1) while y1 and y2 can exceed 0-1, which is exactly how overshoot curves work (a y value greater than 1 momentarily means "further than the destination" before settling back). Reading a bezier's shape: a small x1 with large y1 means the curve rockets away from 0 immediately (fast start), a x2 close to 1 with y2 close to 1 means it approaches the end slowly (a soft landing). This is the literal mechanism behind why "ease-out" curves (fast start, slow finish) suit entrances and "ease-in" curves (slow start, fast finish) suit exits.
The entrance/exit/move convention is not arbitrary tradition, it maps to what the eye expects from real-world motion combined with what each transition communicates. An entering element is arriving to get the user's attention, so a fast initial movement (drawing the eye) that decelerates into its resting place (ease-out, e.g. cubic-bezier(0, 0, 0.2, 1)) both grabs attention immediately and settles predictably, letting the user's eye land on the final position without overshoot-induced uncertainty about where it will actually stop. An exiting element is leaving the user's attention already, so an ease-in curve (slow start, accelerating away, e.g. cubic-bezier(0.4, 0, 1, 1)) matches how the eye naturally disengages, and starting slowly avoids a jarring instant-snap departure while still getting off-screen quickly rather than dawdling. An element MOVING from one place to another (not entering or leaving the screen, just relocating) typically wants ease-in-out (slow-fast-slow, e.g. cubic-bezier(0.4, 0, 0.2, 1)) because both endpoints are already "in the scene" and deserve a soft launch and a soft landing.
Springs are a physically different model from bezier curves: instead of a pre-authored fixed-shape curve over a fixed duration, a spring is a damped harmonic oscillator continuously simulated (mass, stiffness/tension, damping/friction), which naturally produces a duration that emerges from the physics rather than being set directly, and which naturally handles interrupted/re-targeted motion (a spring chasing a new target from its current velocity looks continuous; a bezier tween restarted mid-flight visually snaps or requires manual current-value capture). Springs suit anything that responds to continuous or interruptible input (drag, hover, live data); bezier curves suit anything with a fixed, known start and end that should look identical every time it plays (a scripted entrance sequence, a design-system-consistent modal open).