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.json

Usage

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

  1. Two independent controlled pairs. currentStep / defaultCurrentStep track which frame is active, and playing / defaultPlaying track whether the timeline is auto-advancing. Either pair can be left uncontrolled — the component manages its own state — or controlled by passing onStepChange / onPlayingChange.
  2. setTimeout, not setInterval. The autoplay loop re-schedules itself from inside each tick. This avoids the drift that setInterval accumulates under tab throttling, and cancels cleanly on unmount or when playing flips.
  3. 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 with SPRINGS.smooth.
  4. Play / pause icon morph. The button uses AnimatePresence with mode="wait" and SPRINGS.snap to crossfade between the play and pause glyphs — quick, but never jarring.
  5. Reduced motion. When prefers-reduced-motion: reduce is set, the indicator snaps instantly and autoplay is suppressed the moment it would have started.
  6. Keyboard. With focus inside the scrubber: Space toggles play/pause, / step backward / forward, Home / End jump to the first / last step.

Props

PropTypeDefaultDescription
stepsreadonly { label?: string; description?: string }[]requiredOrdered list of steps. Returns null when empty.
currentStepnumberControlled active step index. Pair with onStepChange.
defaultCurrentStepnumber0Uncontrolled initial step index.
onStepChange(step: number) => voidFires whenever the active step changes.
playingbooleanControlled playing state. Pair with onPlayingChange.
defaultPlayingbooleanfalseUncontrolled initial playing state.
onPlayingChange(playing: boolean) => voidFires when play/pause flips, including at end-of-timeline.
playbackSpeednumber700Milliseconds between autoplayed steps.
loopbooleanfalseWhen true, autoplay wraps from the last step back to the first.
classNamestringMerged onto the outer <div> via cn().

Accessibility

  • The root is role="group" with aria-label="Algorithm playback controls" so assistive tech treats the cluster as a single labelled control surface.
  • Play / Pause carries aria-pressed and switches its aria-label between "Play" and "Pause". Prev, Next, and Restart each have discrete labels and disable cleanly at the edges.
  • The step counter ("3 / 12") lives in an aria-live="polite" region so screen-reader users are read the new step after every change.
  • The hidden <input type="range"> carries aria-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, End work 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-track trackHex colour override (the library uses --cb-accent), the playSound('tap') peer dep, and the project-specific workbench ControlSchema export.