Gradient Step On Curve
One-dimensional gradient descent on the loss parabola L(w) = (w − wOpt)². A dot sits on the curve at the current weight. The learner tunes a learning-rate slider and taps Step to apply one update — w_new = w − lr × dL/dw — and watches the dot spring along the curve toward (or past) the minimum.
A live tangent line shows the local slope at the probe, a faint horizontal arrow previews where the next step will land, and the walker auto-classifies its behaviour as start / stepping / converged / overshooting / diverging from the loss trajectory — the dot, tangent, trail, and narration all recolour to match.
Small learning rates settle in many tiny steps. Medium learning rates converge cleanly. Large learning rates bounce past the minimum. Very large learning rates diverge — the loss grows step over step and the dot turns red.
Loss is 9.0 at w = 0. The slope says go right. Choose a learning rate and click Step.
Installation
npx shadcn@latest add https://craftbits.dev/r/gradient-step-on-curve.jsonUsage
import { GradientStepOnCurve } from "@craft-bits/viz/gradient-step-on-curve";
<GradientStepOnCurve />Pre-load a learning rate that will overshoot:
<GradientStepOnCurve defaultLearningRate={0.4} />Re-key the parabola — descend toward w = 5:
<GradientStepOnCurve startWeight={1} optimalWeight={5} />Subscribe to step events to drive an external loss-vs-step chart:
<GradientStepOnCurve
onStep={(history) => {
/* read history.map((h) => h.loss) */
}}
/>Understanding the component
- The plot. A wide SVG renders the
w ∈ [−1, 7],L ∈ [0, 16]window with a faint grid and a dashed target marker at(wOpt, 0). The parabola is sampled once on first render — its shape never changes during a step. - The dot. The probe is a glowing filled circle that sits on the curve. Step animation interpolates
wfrom start to end and imperatively writescx/cyon the dot, glow, and tangent line via motion'sanimate()— no React re-render per frame. - Tangent line. A short dashed segment passes through the probe with slope
2(w − wOpt). It updates live during the step so the slope visibly flattens near the minimum and flips sign on overshoot. - Step preview. A horizontal dashed arrow at the bottom of the plot shows where
wwill land if the learner taps Step at the current learning rate. The arrow lengthens withlrand shrinks as the gradient magnitude decreases. - Phase machine. The walker classifies its own behaviour from the loss trajectory:
start(only one record),stepping(loss decreasing),converged(loss below threshold),overshooting(the probe crossedwOpt),diverging(loss grew significantly). The phase drives every accent colour and the narration copy. - Trail. Every visited weight is drawn as a small ghost dot connected by a dashed polyline. The trail recolours along with the phase.
- In-SVG learning-rate slider. The slider lives at the bottom of the SVG with ticks at the canonical sweet spots. Pointer drag and keyboard both move it; Space or Enter while focused triggers Step so the entire interaction is keyboard-reachable.
- Reduced motion. Under
prefers-reduced-motion: reduce, the step animation collapses to an instant attribute set, and the idle, converged, and divergence pulses all disable.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
startWeight | number | 0 | Initial weight. Clamped to the visible domain. |
optimalWeight | number | 3 | The minimum of L(w) = (w − optimalWeight)². |
defaultLearningRate | number | 0.1 | Initial slider value. Clamped to the slider range. |
convergeThreshold | number | 0.05 | Loss below this enters the converged phase. |
maxSteps | number | 100 | Hard cap on iterations before Step disables. |
transition | Transition | SPRINGS.smooth | Override the step animation transition. |
onStep | (history) => void | — | Fires after each step, post-animation, with 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 current weight, loss, learning rate, and step count. - The in-SVG learning-rate slider is
role="slider"with min/max/now/text set, full keyboard support (arrow keys for plus/minus a step, Shift for five-step jumps, Home and End for bounds), and a visible focus ring. - Space or Enter on the focused slider triggers Step — the entire walker is operable without a mouse.
- A live region below the buttons announces weight, loss, learning rate, and phase after each step. It mutes while the slider is being dragged to avoid noisy updates.
- The narration paragraph also has
aria-live="polite"and reads as plain prose; it is the canonical explanation for each phase. - Colour is never the only signal — the 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 idle, converged, and divergence pulses all disable.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/GradientStepOnCurve.tsx). The source was a tightly bundled lesson component — it consumedSvgLabelandChallengeBtnfrom the lesson chrome, hardcoded the parabola at(w − 3)², depended on per-track lesson palette tokens, and inlined its own ad-hoc spring names. The viz extract drops the lesson chrome, generalises the parabola viastartWeightandoptimalWeightprops, remaps the colour palette tovar(--cb-*)semantic tokens so consumer themes repaint freely, and re-keys the step animation to the canonicalSPRINGS.smoothso all motion comes from the same place as every other craft-bits component.