Shots in this scene
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.
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-noneorjump-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
Count the positions
In the first example press Replay, pause at once and step to 300 ms. Read the lanes:
jump-end0 px (it has not jumped yet),jump-start131 px (25%),jump-none174.7 px (33%),jump-both209.6 px (40%). At 1.7 sjump-nonehas reached 524 px, the end, after its third jump at 1.5 s, whilejump-endis only at 393 px (75%). - 2
Double the steps
Change
steps(4, …)tosteps(8, …)in all four rules.jump-endnow sits at 0, 12.5%, 25%… and the lanes look closer to a smooth move.
Code & diagrams
Replay, pause and step. Read each lane's position at 300 ms, 800 ms and 1.3 s, and look at the marks.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.The lamp sprite is a data URI built from six SVG frames. Press Replay and step through the lamp.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
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.
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.
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: pixelatedfor 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
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
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 writtenend): 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
See the numbers. For a move from 0% to 100% in 4 steps:
jump-endshows 0, 25, 50, 75 (and 100 after the end).jump-startshows 25, 50, 75, 100.jump-noneshows 0, 33, 67, 100.jump-bothshows 20, 40, 60, 80 (and 100 after). In the first example the lane is 524 px wide, sojump-noneshows 0, 174.7, 349.3 and 524 px. - 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 inchunits with a monospace font. - 5
Validity.
steps(0)is invalid, andsteps(1, jump-none)too (it needs 2 or more steps). In theanimationshorthand an invalid timing function makes the whole declaration invalid, so nothing animates. In theanimation-timing-functionlonghand only that one value is dropped, so the defaulteaseis used. Both fail without any message. - 6
Cost and care. A sprite sheet animates
background-position, which repaints (the measured table says paint). The timeline lists the track asbackground-position-xand 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
What is the difference between jump-end and jump-none in steps(4, …)?
How do you show each of 6 sprite frames exactly once?
Why does steps(0) in the animation shorthand stop all animation?
Practice
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.
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)showsYou can build a sprite-sheet animation with the right distance and step count
You know an invalid
steps()in the shorthand drops the whole animationYou know ticking loops need a reduced-motion rule