Lookahead Ghost
A three-phase visualisation of Nesterov momentum on the parabola L(x) = (x − xOpt)². On every Step, a translucent ghost slides forward to the lookahead position, the gradient is drawn at the ghost (not at the current position), the learner pauses to compare it against the gradient that would be at the dot, and finally the real dot springs along the freshly-computed velocity.
The aha moment is the second phase. When the ghost crosses the minimum, the gradient evaluated at the ghost reverses sign — and Nesterov uses that gradient, not the present one. Overshoots get caught preemptively rather than after the fact.
Classical momentum computes the gradient at your current position. Nesterov asks: why not check where you're about to land?
Installation
npx shadcn@latest add https://craftbits.dev/r/lookahead-ghost.jsonUsage
import { LookaheadGhost } from "@craft-bits/viz/lookahead-ghost";
<LookaheadGhost />Start the dot somewhere else and reshape the bowl:
<LookaheadGhost defaultX={11} xOpt={4} />Push β toward overshoot territory:
<LookaheadGhost beta={0.95} learningRate={0.12} />Subscribe to step events to drive an external chart:
<LookaheadGhost
onStep={(history) => {
/* read history.map((h) => h.loss) */
}}
/>Understanding the component
- The bowl. A plot renders the quadratic
L(x) = (x − xOpt)²as a smooth path with grid, axes, and a dashed crosshair atxOptmarking the minimum. The startingxdefaults to9so the early steps are dramatic. - Phase 1 — ghost-appear. When the learner clicks
Step, a translucent ghost dot fades in at the current position, then slides along the parabola to the lookaheadcurX + β · curV. A dashed connector from the real dot to the ghost makes the displacement obvious. - Phase 2 — gradient-show. The gradient arrow grows out of the ghost position with a snappy spring. Two annotations appear:
gradient here:(at the ghost, the one Nesterov actually uses) andgradient would be:(at the current dot, the one classical momentum would use). When the ghost has already crossed the minimum, apast minimum!warning surfaces. - Phase 3 — step-move. A brief beat lets the learner read the annotations, then the ghost, line, and arrow fade out and the real dot springs to its new position. The animation is driven by motion's
animate()writing raw SVG attributes so the SVG never re-renders mid-frame. - Narration machine. The component derives a narration phase from the step count and whether the dot has crossed the minimum:
observe→first-ghost→correction/overshoot→insight. The phase drives the narration prose, the accent stroke colour of the readouts, and the live-region copy. - Trail. Every previously visited position is rendered as a small dot connected by a dashed polyline; opacity fades in from oldest to newest so the eye reads the trajectory.
- Idle pulse. During the
observephase only, a soft pulsing ring radiates from the dot to invite the first click. The pulse disables underprefers-reduced-motion. - Reduced motion. Under
prefers-reduced-motion: reduce, all three phases collapse to instant attribute updates and the pulse disables.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultX | number | 9 | Initial position. Clamped to the plot domain. |
xOpt | number | 3 | Location of the minimum. L(x) = (x − xOpt)². |
learningRate | number | 0.1 | Step size applied to the gradient at the ghost. |
beta | number | 0.9 | Nesterov momentum coefficient. 0 collapses to vanilla GD. |
ghostTransition | Transition | SPRINGS.smooth | Override the ghost slide animation. |
stepTransition | Transition | SPRINGS.smooth | Override the real-dot step animation. |
arrowTransition | Transition | SPRINGS.snap | Override the gradient-arrow grow animation. |
onStep | (history) => void | — | Fires after each completed step 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 position, velocity, step count, learning rate, and β. - The
Stepbutton is the sole interactive control; it disables for the duration of the three-phase animation and re-labels per phase (Ghost appearing…,Gradient at ghost…,Stepping…). - A live region below the buttons announces position, velocity, and the current narration phase after each step.
- The narration paragraph itself is
aria-live="polite"and reads as plain prose — the canonical explanation per phase. - Phase is never encoded by colour alone: the narration prose and live-region status text also identify the phase.
- Motion respects
prefers-reduced-motion: reduce— the three animation phases collapse to instant attribute updates and the observe-phase pulse disables.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/LookaheadGhost.tsx). The source consumed lesson-only chrome (SvgLabel,ChallengeBtn), depended on lesson palette tokens, and inlined a customSPRINGS.gentlename not in the canonical motion module. The viz extract drops the lesson chrome, maps the palette onto thecb-*semantic tokens so consumer themes repaint freely, and re-keys every animation to the canonicalSPRINGS.smooth/SPRINGS.snapso all motion comes from the same place as every other craft-bits component. ThexOpt,learningRate, andbetaare now configurable props instead of hardcoded constants.