Paace Running Animation

July 17, 2026

Paace has a little runner in its header that speeds up as you do. Here it is in the actual app, the runner shifting gait as the pace picks up:

The header : the runner changing gait with your pace.

It isn't one animation played faster, it's six separate Lottie clips, one per intensity band, and the trick is swapping between them cleanly. Drag the slider to try it yourself:

Loading…
Intensity
60%

Six clips, not one

Speeding up a single clip just makes it look sped-up, the stride length stays wrong. So I exported six runs from Jog to Max Sprint, each with its own posture and cadence, and pick one based on the current intensity:

const CLIPS = [speed_0, speed_20, speed_40, speed_60, speed_80, speed_100];

// intensity 0–100 → snap to the nearest of the six clips
const level = Math.min(Math.round(intensity / 20), CLIPS.length - 1);

Swapping on the loop boundary

Swapping the clip the instant the intensity changes cuts the current stride mid-air, and it reads as a jump. So I stopped swapping right away. I keep two values instead: the level I want, which follows the slider immediately, and the level that's actually showing, which lags behind on purpose.

const target = levelFor(intensity); // tracks the slider live
const [displayed, setDisplayed] = useState(target);

Every clip just loops on its own, and the only place displayed catches up to target is at the end of a loop. If they already match there's nothing to do — the clip loops again by itself, so there's no replay to wire up.

// fires once per stride — the one safe moment to change gait
function handleLoopComplete() {
  if (targetRef.current !== displayedRef.current) {
    setDisplayed(targetRef.current);
  }
}

The reason this works is that the feet are planted at the start and end of every cycle. Remounting the next clip (keyed on displayed) restarts it from frame 0 right as the old stride resets, so the feet line up across the cut. You change pace, the runner finishes the step it's on, and picks up the new gait at the top of the next one instead of teleporting.

That's the whole thing — six clips and a swap that always waits for the loop to come around.