← Back to all tutorials

Animation Timing Functions

Master animation easing curves — ease, linear, ease-in, ease-out, ease-in-out, steps(), and custom cubic-bezier.

Animation Timing Functions

The animation-timing-function (and transition-timing-function) controls the speed curve of an animation — how it accelerates and decelerates over time. This is what makes animations feel natural or mechanical.

Built-in Timing Functions

ValueBehaviorBest For
easeSlow start, fast middle, slow endGeneral purpose (default)
linearConstant speed throughoutSpinners, progress bars
ease-inStarts slow, ends fastElements leaving the viewport
ease-outStarts fast, ends slowElements entering the viewport
ease-in-outSlow start and end, fast middleAlternating/looping animations

Comparing Timing Functions

.box-1 { animation: slideRight 2s ease; }
.box-2 { animation: slideRight 2s linear; }
.box-3 { animation: slideRight 2s ease-in; }
.box-4 { animation: slideRight 2s ease-out; }
.box-5 { animation: slideRight 2s ease-in-out; }

All five boxes travel the same distance in the same time, but their acceleration patterns are completely different.

When to Use Which

ScenarioBest TimingWhy
Hover effectsease or ease-outQuick response, smooth finish
Element entering screenease-outArrives quickly, settles gently
Element leaving screenease-inSlow departure, exits fast
Continuous rotationlinearConstant speed is expected
Floating/breathingease-in-outSymmetrical, natural rhythm

Custom cubic-bezier()

For complete control, define your own curve with cubic-bezier(x1, y1, x2, y2):

/* Snappy entrance */
animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);

/* Overshoot (bouncy feel) */
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);

/* Dramatic slow-start */
animation-timing-function: cubic-bezier(0.6, 0, 0.4, 1);

The four values are control points on a Bézier curve. Use Chrome DevTools or cubic-bezier.com to design curves visually.

The steps() Function

steps() creates a frame-by-frame animation instead of smooth interpolation:

/* Jump through 4 distinct frames */
animation-timing-function: steps(4);

/* Sprite sheet animation */
@keyframes walk {
    from { background-position: 0 0; }
    to   { background-position: -512px 0; }
}

.character {
    width: 64px;
    height: 64px;
    background: url('spritesheet.png');
    animation: walk 0.8s steps(8) infinite;
}

steps() Options

SyntaxBehavior
steps(4, jump-start)Jumps at the start of each interval
steps(4, jump-end)Jumps at the end of each interval (default)
steps(4, jump-both)Jumps at both start and end
steps(4, jump-none)No jump at start or end
step-startSame as steps(1, jump-start)
step-endSame as steps(1, jump-end)

Practical: Typewriter Effect

@keyframes typing {
    from { width: 0; }
    to   { width: 20ch; }
}

@keyframes blink {
    50% { border-color: transparent; }
}

.typewriter {
    font-family: monospace;
    overflow: hidden;
    white-space: nowrap;
    border-right: 3px solid #333;
    width: 0;
    animation: typing 3s steps(20) forwards,
               blink 0.7s step-end infinite;
}

Debugging Timing in DevTools

  1. Open Chrome DevTools → Elements panel
  2. Select an animated element
  3. In the Styles pane, click the purple curve icon next to the timing function
  4. A visual editor opens where you can drag control points to adjust the curve

Key Takeaways

  • ease is the default — good for most cases
  • ease-out for entrances, ease-in for exits, linear for constant motion
  • cubic-bezier() gives complete control over the acceleration curve
  • steps() creates frame-by-frame animations — perfect for sprite sheets and typewriter effects
  • Use Chrome DevTools' visual editor for designing custom curves