Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 4.4Beginner ~21 min · shot 4 of 6

steps() and Sprite Sheets

In one line

steps(n) replaces a smooth easing with n jumps. It is how sprite sheets, ticking clocks and typewriters work, and the four jump terms decide whether the first and last values are ever shown.

0/6 · 0%

Think of it like this

A staircase against a ramp. A ramp (an easing curve) lets you slide smoothly from the bottom to the top. A staircase (steps()) has flat parts and sudden rises. You are always on a step, never between two.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

steps()
An easing function that jumps between n values instead of changing smoothly.
jump term
The second argument of steps(): jump-start, jump-end, jump-none or jump-both. It says where the jumps happen.
sprite sheet
One image holding many frames side by side. You show one frame at a time by moving the image.
frame (sprite)
One picture of a sprite sheet. Not to be confused with a screen frame.
ch
A CSS unit: the width of the digit 0 in the current font. In a monospace font it is the width of every letter.

Step by step

01Four ways to jump

Replay the first example and step. The four lanes use the same 2 s move with steps(4, …). Tick marks show 0, 25, 50, 75 and 100%. jump-end lags behind: it sits at 0 for the first half-second. jump-start is ahead: it is already at 25% at time 0.

02A sprite sheet

In the second example the lamp is a 360 × 60 px sheet of six frames, shown in a 60 px window. The animation slides the sheet from 0 to -300 px with steps(6, jump-none): six positions (0, -60 … -300), one frame each. Step through it: 0 rays, 1, 2 … 5.

03A clock and a typewriter

The clock hand turns 360° in 6 s with steps(12, end): a tick every half second. The word types in 1 s with steps(8, end) on a width from 0 to 8ch. The caret blinks with steps(1, end): it switches on and off with no fade.

04Check the count

Count the positions you see before you trust a sheet.

Try it yourself

  1. 1

    Count the positions

    In the first example press Replay, pause at once and step to 300 ms. Read the lanes: jump-end 0 px (it has not jumped yet), jump-start 131 px (25%), jump-none 174.7 px (33%), jump-both 209.6 px (40%). At 1.7 s jump-none has reached 524 px, the end, after its third jump at 1.5 s, while jump-end is only at 393 px (75%).

  2. 2

    Double the steps

    Change steps(4, …) to steps(8, …) in all four rules. jump-end now sits at 0, 12.5%, 25%… and the lanes look closer to a smooth move.

Code & diagrams

Four jump terms

Replay, pause and step. Read each lane's position at 300 ms, 800 ms and 1.3 s, and look at the marks.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.
A sprite sheet, a ticking clock, a typewriter

The lamp sprite is a data URI built from six SVG frames. Press Replay and step through the lamp.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

A sprite sheet that is torn

In the second example, change steps(6, jump-none) to steps(6). Press Run, then Replay, and step to 250 ms.

terminal
$ Step through the lamp
── what you'll see ──
The lamp window shows two frames at once: the end of one frame (a ray at the left edge) and the start of the next (the lamp body). The background offsets are 0, -50, -100, -150, -200, -250 px, then -300 at the end.
Measured: `background-position-x` is -100px at 250 ms. Frames are 60 px wide, so -100 is in the middle of frame 2.

Break #2

A steps() that is not valid

Change steps(6, jump-none) to steps(0) in the shorthand animation: lamp 600ms steps(0) forwards.

terminal
$ Press Replay
── what you'll see ──
The lamp does not animate and no track appears. As a longhand (`animation-timing-function: steps(0)`) the animation runs, but with the default `ease`.
Measured: `CSS.supports("animation-timing-function", "steps(0)")` is false, and so is `steps(1, jump-none)`. `steps(2, jump-none)` is true.

Myth vs fact

Myth

steps(4) shows four values: 0, 25, 50, 75 and 100.

Fact

With the default jump term it shows 0, 25, 50, 75 during the animation. The last value appears only when the animation ends (and is held by the fill mode).

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    A sprite sheet scaled to a non-integer size or placed at a fractional position can show a hair of the next frame, or blur. Keep the frame size an exact number of CSS pixels and use image-rendering: pixelated for pixel art. (We could not reproduce a blur or a sliver here, so treat this as a precaution, not a measured fact.)

  • ▸

    For a sprite with many frames the sheet is one image request, while separate images are many. Provide an aria-label (or a visible label) because a background image has no text.

Remember this

  1. 1

    The problem this solves: some motion should not be smooth. A clock hand ticks. A typewriter shows whole letters. A sprite sheet is a strip of pictures where you must see exactly one picture at a time, never half of two. A smooth easing would blur all of these.

  2. 2

    The function. steps(4) splits the animation's time into 4 equal parts and holds one value for each. The second argument, the jump term, says where the jumps are. jump-end (the default, also written end): jump at the end of each part, so the start value shows first and the end value only at the very end. jump-start: jump at the start of each part, so the end value shows already in the last part. jump-none: no jump at either end, so both the first and last values show and there are n-1 jumps. jump-both: jumps at both ends, so neither value shows during the animation.

  3. 3

    See the numbers. For a move from 0% to 100% in 4 steps: jump-end shows 0, 25, 50, 75 (and 100 after the end). jump-start shows 25, 50, 75, 100. jump-none shows 0, 33, 67, 100. jump-both shows 20, 40, 60, 80 (and 100 after). In the first example the lane is 524 px wide, so jump-none shows 0, 174.7, 349.3 and 524 px.

  4. 4

    Which one to use. A sprite sheet with n frames and the sheet sliding to the last frame: steps(n, jump-none), over a distance of (n-1) frame widths, shows each frame once. A clock hand: steps(12, end) over 360°. A typewriter: steps(characters, end) on a width in ch units with a monospace font.

  5. 5

    Validity. steps(0) is invalid, and steps(1, jump-none) too (it needs 2 or more steps). In the animation shorthand an invalid timing function makes the whole declaration invalid, so nothing animates. In the animation-timing-function longhand only that one value is dropped, so the default ease is used. Both fail without any message.

  6. 6

    Cost and care. A sprite sheet animates background-position, which repaints (the measured table says paint). The timeline lists the track as background-position-x and colours it as paint. A small sprite is cheap. A pause or reduced-motion rule should stop looping ticks and carets: a blinking caret that never stops is attention-grabbing motion with no end.

Explain it without notes

01

What is the difference between jump-end and jump-none in steps(4, …)?

02

How do you show each of 6 sprite frames exactly once?

03

Why does steps(0) in the animation shorthand stop all animation?

Practice

01

Make a loader of 4 dots where the number of visible dots goes 0, 1, 2, 3 and repeats every 1.2 s, using steps() on a clipped width.

02

A clock hand must tick once a second. Write the animation.

Done when you can

  • You can list the values steps(4, jump-end | jump-start | jump-none | jump-both) shows

  • You can build a sprite-sheet animation with the right distance and step count

  • You know an invalid steps() in the shorthand drops the whole animation

  • You know ticking loops need a reduced-motion rule