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.
The dot starts far from the minimum at x = 3. Velocity is zero — no momentum yet.
Installation
npx shadcn@latest add https://craftbits.dev/r/momentum-playback.jsonUsage
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
- The number line. A 560 × 240 SVG renders a horizontal axis from
-2to12. Tick marks call out-2, 0, 2, 3, 4, 6, 8, 10, 12. The tick atxOptis recoloured tovar(--cb-success)and a dashed crosshair marks the minimum. - 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.
- 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.
- 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.
- 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'sanimate()drives the SVGcxattribute directly — no re-render per frame. - 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 usesvar(--cb-warning); "insight" usesvar(--cb-success). - Step arithmetic. A line above the plot shows the literal recurrence from the most recent step. Makes the math live for the learner.
- Reduced motion. Under
prefers-reduced-motion: reduce, the step animation collapses to an instant attribute set, and the observe / insight pulses both disable.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultX | number | 10 | Starting x position on the number line. Reset returns here. |
xOpt | number | 3 | Target x — the minimum of L(x) = (x − xOpt)². |
learningRate | number | 0.1 | Learning rate applied to the velocity term. |
beta | number | 0.9 | Momentum coefficient — fraction of previous velocity carried forward. |
transition | Transition | SPRINGS.smooth | Override the step animation transition. |
onStep | (history) => void | — | Fires after each step (post-animation) with the full history. |
onReset | () => void | — | Fires when the user clicks Reset. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The plot SVG is
role="img"with anaria-labelsummarising the currentx, 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 consumedSvgLabelandChallengeBtn, depended on per-track lesson palette tokens, and inlined ad-hoc spring names. The viz extract drops the lesson chrome, remaps the colour palette tovar(--cb-*)semantic tokens so consumer themes repaint freely, and re-keys the step animation to the canonicalSPRINGS.smooth.