Full Loop Stepper

A four-step, click-through visualizer of the complete training loop on the toy model y = w · x. The learner taps Forward to compute the prediction, Loss to measure error, Gradient to see the descent direction, and Update to slide w one step closer to the target. Each completed cycle drops a dot onto a loss-history strip and the narration recolours from observemeasuringupdatinginsight.

With the defaults — x = 1, y_target = 2, lr = 0.1 — the optimal weight is 2.0. Starting at w = 0.5, the loop reaches the insight phase in around five clicks: the loss bar shrinks, the dot lands on the target marker, and the narration declares the loop done.

Full training loop stepper. Forward, loss, gradient, update.y = w · x = 0.50 · 1.00ForwardLossGradientUpdate
Cycle 0, step forward. w = 0.50, loss = 0.000.

Click Forward to make a prediction with the current weight. The target function has slope 2.00, so the weight should converge there.

Customize
Loop
0.50
2.00
0.10
0.10

Installation

npx shadcn@latest add https://craftbits.dev/r/full-loop-stepper.json

Usage

import { FullLoopStepper } from "@craft-bits/viz/full-loop-stepper";
 
<FullLoopStepper />

Start further from the target so the user sees a longer walk:

<FullLoopStepper defaultWeight={0} learningRate={0.05} />

Subscribe to cycle completions to drive an external chart:

<FullLoopStepper
  onCycleComplete={(record) => {
    /* read record.loss / record.wAfter */
  }}
/>

Understanding the component

  1. Four-step strip. Across the top, four rounded boxes — Forward, Loss, Gradient, Update — represent the steps of one cycle. The active step lights up in its semantic colour; completed steps fade to a muted state. A loop glyph appears after the strip once a cycle has completed.
  2. Arithmetic readout. A single mono-font line at the top updates per step to show the live computation — first y = w · x, then the loss expression, then the full update equation.
  3. Number line for w. A horizontal axis from 0 to 3. The dashed green vertical marker is the target weight. The accent-coloured dot is w — it animates with a smooth spring on update and pulses on forward.
  4. Gradient arrow. During the gradient and update steps, a warning-coloured arrow appears below the number line pointing in the direction of the negative gradient. Its length is fixed; the magnitude lives on the readout.
  5. Loss bar. A horizontal bar below the number line fills proportionally to the current loss, capped at the visual ceiling. Its width is tweened via motion's animate() on the loss step — no full re-render per frame.
  6. Cycle history strip. Each completed cycle drops a small accent dot underneath the loss bar with the cycle's final loss labelled in mono. Dot opacity reflects how close that cycle's loss was to zero.
  7. Phase machine. The component classifies its own state into observe / measuring / updating / insight from the cycle count and the most recent loss. The phase tints the narration background and triggers the celebration ring once the loop converges.
  8. Reduced motion. Under prefers-reduced-motion: reduce, every animation collapses to an instant state change so the lesson is fully usable without motion.

Props

PropTypeDefaultDescription
defaultWeightnumber0.5Initial value of w on the number line.
targetWeightnumber2.0Position of the dashed target marker.
xnumber1Training input — used to compute the prediction.
yTargetnumber2Training label — used to compute the loss.
learningRatenumber0.1Multiplied into the gradient on the update step.
convergeThresholdnumber0.1Loss below this enters the insight phase.
insightCyclesnumber5Cycle count that also triggers the insight phase.
transitionTransitionSPRINGS.smoothOverride the w-dot slide transition.
onStep(step, snapshot) => voidFires after each step inside a cycle.
onCycleComplete(record) => voidFires after a full cycle completes.
onReset() => voidFires when the user clicks Reset.
classNamestringMerged onto the root via cn().

Accessibility

  • The SVG canvas is role="img" with an aria-label summarising the current weight, cycle count, and target.
  • The step and reset buttons are real <button type="button"> elements with focus-visible rings and a tap-down scale.
  • A live region below the buttons announces the current cycle, active step, weight, and most recent loss after every change.
  • 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; the active step is named in the live region and the narration, and the loss value is read out alongside the bar.
  • Motion respects prefers-reduced-motion: reduce — every animation collapses to an instant state change.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/math/FullLoopStepper.tsx). The source was a tightly bundled lesson primitive — it consumed SvgLabel and ChallengeBtn from the lesson chrome, hardcoded the toy model, and inlined its own ad-hoc spring names. The viz extract drops the lesson chrome, promotes every magic number to a typed prop, remaps the palette to var(--cb-*) semantic tokens so consumer themes repaint freely, and re-keys all motion to the canonical SPRINGS entries.