Two animations moving an element across the exact same distance, over the exact same duration, can still feel completely different: one looks mechanical and artificial, the other feels natural, almost physical. That difference comes entirely down to the timing function, the curve that controls the animation's speed at every instant.
Whether on a transition or a CSS animation, the timing function always answers the same question: should the element move at a constant speed, start slow and speed up, or the other way around?
Definition
The timing function, often called an easing curve, defines how an animation's speed changes between its start and its end. It gets set with predefined keywords, like linear or ease, or with the cubic-bezier() function, which draws an entirely custom curve.
| Keyword | Behavior |
|---|---|
linear | Constant speed from start to finish |
ease | Fast start, slowing down near the end |
ease-in | Slow start, gradual acceleration |
ease-out | Fast start, gentle arrival |
ease-in-out | Slow at both ends, fast in the middle |
Why linear feels mechanical
linear moves an element at a strictly constant speed, with no variation at all. That behavior matches very few real-world motions: in nature, almost everything that moves speeds up at the start and slows down at the end, if only because of inertia. A linear motion, even when perfectly smooth technically, immediately reads as robotic or mechanical rather than as a natural gesture.
.spinner {
animation: spin 1s linear infinite;
}
.notification {
transform: translateY(0);
transition: transform 0.3s ease-out;
}ease-out for an entrance, ease-in for an exit
Choosing the right curve follows a simple logic, rooted in how humans perceive motion. For an element appearing or arriving at its destination, ease-out feels the most natural: it starts fast then gently slows down as it approaches its final position, like an object settling into place. For an element disappearing or leaving, on the other hand, ease-in, which starts slow then speeds up, matches intuition better.
ease-out remains the safest default for the vast majority of interface interactions, like a menu unfolding or a card growing on hover: the result feels natural almost every time, with no extra tuning needed.Drawing a custom curve with cubic-bezier()
When the five predefined keywords are not enough, cubic-bezier() accepts four numbers defining a cubic Bézier curve, a mathematical curve also used in vector drawing software. Some dramatic transform effects, like a slight overshoot at the end of a motion, are only possible with a custom curve like this, sometimes even using values that temporarily exceed 100% to simulate a small bounce before settling.
Most browser developer tools even include a visual curve editor with a live preview, which is far easier to fine-tune than guessing the four numbers by hand.
Frequently asked questions
cubic-bezier() alone cannot reproduce a bounce with several oscillations, since it describes a single continuous curve with no change of direction. A realistic bounce effect needs several steps inside a @keyframes rule, each with its own timing function, or a dedicated JavaScript motion library.
No, the timing function never changes the total duration, which is set separately through transition-duration or animation-duration. It only reshapes how speed gets distributed within that fixed duration, not its overall length.
Because fast motion or overly pronounced curves can cause real discomfort, even nausea, for users sensitive to movement. A well-built site detects that preference through a dedicated media query and then significantly reduces, or removes entirely, the animations involved.