Momentum Playback

A 1D playback of gradient descent with momentum on L(x) = (x − xOpt)². A position dot sits on a number line spanning [-2, 12]. Above the line, the velocity arrow shows the accumulated direction. Below the line, the raw gradient arrow shows the local signal. As the learner clicks Step, ghost arrows trail behind the velocity arrow to show how momentum accumulates.

The recurrence is v_new = β·v_old + g, x_new = x − lr·v_new. With the defaults (lr = 0.1, β = 0.9, xOpt = 3), the dot builds velocity, overshoots x = 3, and oscillates back. The "insight" phase narrates the takeaway: velocity averages recent gradients — consistent signals build up, oscillations cancel.

Momentum playback on L of x equals x minus 3 squared.
Step 0. Position: x = 10, velocity = 0, gradient = 14.

The dot starts far from the minimum at x = 3. Velocity is zero — no momentum yet.

Customize
Momentum
10.0
3.0
0.10
0.90

Installation

npx shadcn@latest add https://craftbits.dev/r/momentum-playback.json

Usage

import { MomentumPlayback } from "@craft-bits/viz/momentum-playback";
 
<MomentumPlayback />

Tighten the momentum to see how the dot behaves with a smaller β:

<MomentumPlayback beta={0.5} />

Subscribe to step events:

<MomentumPlayback
  onStep={(history) => {
    /* read history.map((h) => ({ x: h.x, v: h.v })) */
  }}
/>

Understanding the component

  1. The number line. A 560 × 240 SVG renders a horizontal axis from -2 to 12. Tick marks call out -2, 0, 2, 3, 4, 6, 8, 10, 12. The tick at xOpt is recoloured to var(--cb-success) and a dashed crosshair marks the minimum.
  2. The dot. The blob on the line is the optimiser's current position. Its colour reflects the current phase. A soft glow under the dot is driven by the same fill colour at lower opacity through a Gaussian-blur filter.
  3. Velocity lane (above). The current velocity is drawn as an arrow anchored at the dot, pointing in the direction the dot will travel next. Above it, ghost arrows stack vertically: each ghost is anchored at the x-position where that velocity lived, with the most recent ghost the brightest.
  4. Gradient lane (below). The current gradient arrow is drawn below the line. Unlike the velocity arrow, the gradient depends only on the dot's current position — it shrinks as the dot approaches the minimum.
  5. Step. The action button computes the next position via the recurrence above, then springs the dot from the current position to the new one. motion's animate() drives the SVG cx attribute directly — no re-render per frame.
  6. Phase machine. The narration steps through five phases based on step count: observe (no steps), first-step, building (steps 2–3), approaching (steps 4–7), insight (8+). Each phase recolours the dot, position readout, step counter, and narration background. The "approaching" phase uses var(--cb-warning); "insight" uses var(--cb-success).
  7. Step arithmetic. A line above the plot shows the literal recurrence from the most recent step. Makes the math live for the learner.
  8. Reduced motion. Under prefers-reduced-motion: reduce, the step animation collapses to an instant attribute set, and the observe / insight pulses both disable.

Props

PropTypeDefaultDescription
defaultXnumber10Starting x position on the number line. Reset returns here.
xOptnumber3Target x — the minimum of L(x) = (x − xOpt)².
learningRatenumber0.1Learning rate applied to the velocity term.
betanumber0.9Momentum coefficient — fraction of previous velocity carried forward.
transitionTransitionSPRINGS.smoothOverride the step animation transition.
onStep(history) => voidFires after each step (post-animation) with the full history.
onReset() => voidFires when the user clicks Reset.
classNamestringMerged onto the root via cn().

Accessibility

  • The plot SVG is role="img" with an aria-label summarising the current x, velocity, gradient, learning rate, and beta.
  • The Step and Reset buttons are native <button> elements with visible focus rings.
  • A live region (aria-live="polite") below the buttons announces position, velocity, and gradient after each step.
  • The narration paragraph is aria-live="polite" and reads as plain prose — it is the canonical explanation for each phase.
  • Colour is never the only signal — phase is also encoded in the narration prose and the live-region status text.
  • Motion respects prefers-reduced-motion: reduce — the dot snaps instantly, and the observe / insight pulses both disable.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/math/MomentumPlayback.tsx). The source was bundled with lesson chrome — it consumed SvgLabel and ChallengeBtn, depended on per-track lesson palette tokens, and inlined ad-hoc spring names. The viz extract drops the lesson chrome, remaps the colour palette to var(--cb-*) semantic tokens so consumer themes repaint freely, and re-keys the step animation to the canonical SPRINGS.smooth.