Easing

steps()

steps() is a GSAP ease that quantizes a tween's progress into a fixed number of discrete jumps instead of interpolating smoothly, so the animated value snaps between set positions. It's how you get a mechanical, tick-by-tick feel: typewriter reveals, odometer digits, a clock's second hand, sprite-sheet frames.

Updated July 1, 2026

Mechanics

How steps() works

Every other ease returns a continuous value as the tween plays. steps(n) rounds that value to the nearest of n evenly spaced plateaus, so the property holds, then jumps, n times across the duration. There are no in-between frames. With steps(6) the tween moves through 6 hard jumps and the element never shows a value that sits between two of them.

It's the core SteppedEase, so nothing extra to register. Pass the count in the string form and GSAP does the rest:

script.js

The graph is a staircase, not a curve. That's the whole point: motion that reads as increments, not as sliding. A higher n gives more, smaller ticks (approaching smooth as n climbs); a lower n gives fewer, chunkier jumps.

When

Use it for

  • Typewriter reveals where one character should appear per beat, no fade or slide between them
  • Number counters that should tick through visible integers like an odometer instead of blurring up to the total
  • Sprite-sheet or frame-by-frame animation, where you're stepping a background-position through fixed cells
  • A clock's second hand, a loading tick, a segmented progress bar filling one block at a time
  • Any UI where the intended feel is mechanical and discrete rather than fluid
Alternatives

Use something else when

  • You want smooth, responsive UI motion, that's expo.out or power3.out, not a staircase
  • You need a custom curve with hold-and-release character but not hard snaps, use CustomEase
  • You only need to land values on a grid (nearest 10px, nearest integer) while the motion itself stays smooth, use GSAP's snap utility on the property, not a stepped ease
  • The effect is pure CSS with no timeline logic, native CSS steps() in animation-timing-function does the same job with zero JS

GSAP steps() vs CSS steps()

They produce the same staircase, but the boundary behaviour differs. CSS steps(n, start) jumps at the beginning of each interval and steps(n, end) (the default) jumps at the end. GSAP's steps(n) is a single stepped curve you drop into a tween or timeline, so you get sequencing, pausing, and reversal for free. Most stepped motion in the wild is still CSS: 91.7% of mobile pages ship a CSS transition while only 18.4% load a JS animation library at all (HTTP Archive Web Almanac, 2024).
In production

Used in these Annnimate components

Two Annnimate components live on this discrete, tick-by-tick cadence, even where they reach it through a timeline rather than the ease directly:

  • Typewriter reveals one character at a time so the caret advances in fixed jumps instead of sliding. It builds that with a per-character timeline, and steps() is the single-tween way to get the same mechanical reveal when you don't need per-character timing control.
  • Counter rolls a number up to its target and reformats the value every frame. It rides power3.inOut for a smooth ramp, but swap that for steps() and the digits tick in visible increments, the odometer feel instead of the blur.

Watch the count

The jumps land on GSAP's rounding, not on your data. If you're stepping through N images or N digits, match the ease count to the real number of positions (or the last cell gets skipped or doubled). For counters, a stepped ease changes the LOOK of the roll but the on-screen number still comes from your onUpdate formatting, so round there too or you'll see stepped motion with unstepped decimals.
Used in components

See it running in production

FAQ

Common questions

Does GSAP's steps() need EasePack?
No. steps() is the core SteppedEase, included in gsap itself, so you can write ease: "steps(6)" without importing or registering anything. EasePack adds RoughEase, SlowMo, and ExpoScaleEase, none of which you need for stepped motion. And since GSAP 3.13 every plugin is free anyway, so there's no paywall to worry about either way.
How is GSAP steps() different from CSS steps()?
Same staircase output, different control. CSS steps() lives in animation-timing-function and takes a direction (start or end) that decides whether the jump happens at the front or back of each interval. GSAP's steps(n) is an ease you pass to a tween or timeline, so it inherits GSAP's play/pause/reverse/scrub and sequencing. Reach for CSS steps() on a self-contained keyframe animation, GSAP steps() when the stepped move is part of a larger timeline.
How many jumps does steps(6) actually produce?
Six discrete increments across the tween. The value holds on a plateau, jumps, and repeats until it reaches the end. If your animation looks like it has one too many or too few stops, that's almost always a mismatch between the step count and the number of real positions you're moving through (frames in a sprite sheet, digits in an odometer). Set the count to the number of positions, not a round guess.
Can I use steps() for a typewriter effect?
Yes, if you're revealing a fixed-width block character by character (for example animating a width or a clip that exposes one glyph per step). steps(numberOfCharacters) makes each character appear on its own beat with no fade. For variable-width type or human-typing jitter, a per-character timeline gives you more control, which is how the Annnimate Typewriter is built. steps() is the quick single-tween version.
steps() vs snap for ticking numbers?
Different jobs. steps() quantizes the tween's PROGRESS, so the whole motion moves in jumps at fixed time intervals. GSAP's snap quantizes the VALUE, so the motion can still ease smoothly but only ever lands on allowed values (nearest integer, nearest 10). For an odometer that ticks on a rhythm, steps(). For a value that eases in but must settle on whole numbers, snap the property and keep a smooth ease.