Function
How many frames the guide cycles through
The frame to draw, and the handlers that let the learner move it
Every install guide plays the same way, and this is the one place that says how. Three of them read from it, so a change to the pacing cannot land on one device and miss the others.
What it buys is that the learner does nothing: the flow just runs. What that costs is the learner who looks away — at the control they were sent to find, or at the browser's own menu covering the page — and comes back to a guide that has moved on. A horizontal drag answers that: the frame they missed is one gesture back rather than a whole loop away.
The gesture is a nudge, not a takeover. There is no pause and no manual mode to get stranded in: the loop plays on, and the frame the learner landed on gets a full interval of its own before it does — which is what the per-frame timer below is for, rather than one interval for the whole loop. A frame taken away early punishes the learner for the moment their gesture happened to land in.
Leftwards is forwards, so the finger travels the way the frame controls are laid out.
The three moves are exported because a visible control and the gesture have to do the same thing. Two copies of the wrap arithmetic is two places for "back from the first frame reaches the last" to stop being true.
Under prefers-reduced-motion it does not advance at all. The preference is
read after mount — it cannot be known while rendering on the server — so the
first frame is painted either way and only the movement is conditional. The
drag keeps working there, and is the only thing that does: the preference
silences motion the learner did not ask for, and this is the motion they did.
Client hook: a guide that plays itself, and answers a drag.