Algorithm Scrubber
A timeline controller for stepping through algorithm execution. Provide a steps array — each with an optional label and description — and the component renders play / pause / prev / next / restart, a discrete dot-per-step track, and a caption for the active step. Wire currentStep + onStepChange to drive any visualization above it from the same coordinate space.
1 / 6
Initialise — all visited flags false.
Customize
Timeline
6
Playback
700ms
Installation
npx shadcn@latest add https://craftbits.dev/r/algorithm-scrubber.jsonUsage
import { AlgorithmScrubber } from "@craft-bits/core";
const steps = [
{ label: "init", description: "Set all distances to ∞." },
{ label: "pop A", description: "Visit A, relax outgoing edges." },
{ label: "pop C", description: "Visit C, push neighbors." },
];
<AlgorithmScrubber steps={steps} />Drive a visualization alongside the scrubber with controlled state:
const [step, setStep] = useState(0);
<MyAlgorithmViz frame={frames[step]} />
<AlgorithmScrubber
steps={steps}
currentStep={step}
onStepChange={setStep}
playbackSpeed={500}
loop
/>Understanding the component
- Two independent controlled pairs.
currentStep/defaultCurrentSteptrack which frame is active, andplaying/defaultPlayingtrack whether the timeline is auto-advancing. Either pair can be left uncontrolled — the component manages its own state — or controlled by passingonStepChange/onPlayingChange. setTimeout, notsetInterval. The autoplay loop re-schedules itself from inside each tick. This avoids the drift thatsetIntervalaccumulates under tab throttling, and cancels cleanly on unmount or whenplayingflips.- Native
<input type="range">for the slider. A transparent range input is layered over the visual track, so keyboard dragging, pointer dragging, and ARIA value semantics come for free. The visible dots and indicator are positioned by percent and animated withSPRINGS.smooth. - Play / pause icon morph. The button uses
AnimatePresencewithmode="wait"andSPRINGS.snapto crossfade between the play and pause glyphs — quick, but never jarring. - Reduced motion. When
prefers-reduced-motion: reduceis set, the indicator snaps instantly and autoplay is suppressed the moment it would have started. - Keyboard. With focus inside the scrubber:
Spacetoggles play/pause,←/→step backward / forward,Home/Endjump to the first / last step.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
steps | readonly { label?: string; description?: string }[] | required | Ordered list of steps. Returns null when empty. |
currentStep | number | — | Controlled active step index. Pair with onStepChange. |
defaultCurrentStep | number | 0 | Uncontrolled initial step index. |
onStepChange | (step: number) => void | — | Fires whenever the active step changes. |
playing | boolean | — | Controlled playing state. Pair with onPlayingChange. |
defaultPlaying | boolean | false | Uncontrolled initial playing state. |
onPlayingChange | (playing: boolean) => void | — | Fires when play/pause flips, including at end-of-timeline. |
playbackSpeed | number | 700 | Milliseconds between autoplayed steps. |
loop | boolean | false | When true, autoplay wraps from the last step back to the first. |
className | string | — | Merged onto the outer <div> via cn(). |
Accessibility
- The root is
role="group"witharia-label="Algorithm playback controls"so assistive tech treats the cluster as a single labelled control surface. - Play / Pause carries
aria-pressedand switches itsaria-labelbetween"Play"and"Pause". Prev, Next, and Restart each have discrete labels and disable cleanly at the edges. - The step counter (
"3 / 12") lives in anaria-live="polite"region so screen-reader users are read the new step after every change. - The hidden
<input type="range">carriesaria-valuetext("Step 3 of 12: pop C") — drag and arrow-key consumers hear what each frame represents instead of bare numbers. - Keyboard support is implemented at the root:
Space,←,→,Home,Endwork without focusing a specific button. - Motion respects
prefers-reduced-motion: the indicator snaps and autoplay is suppressed.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/viz/AlgorithmScrubber.tsx). Generalised — stripped the inline visualization slot, the per-tracktrackHexcolour override (the library uses--cb-accent), theplaySound('tap')peer dep, and the project-specific workbenchControlSchemaexport.