Gradient Pipeline
A visceral, click-through demo of the vanishing-gradient problem. A gradient signal starts at the loss on the right of the pipeline and travels backward through each layer. At every layer the pulse is multiplied by the local derivative ∂a/∂z — the radius shrinks, the opacity dims, and the colour shifts from the accent (healthy) hue through warning to error (vanishing). By the time the pulse reaches the leftmost layer, the eye sees the gradient is barely there.
Generic over the layer set. Pass any array of { label, fn, derivative } and the component renders the boxes, arrows, and running-gradient labels itself.
3 layers, each with its own local derivative. The loss has been computed — now the gradient must travel backward through every layer to reach the weights. Click to send the signal.
Installation
npx shadcn@latest add https://craftbits.dev/r/gradient-pipeline.jsonUsage
import { GradientPipeline } from "@craft-bits/viz/gradient-pipeline";
<GradientPipeline />;Custom layer set (a 5-layer pipeline that vanishes harder):
<GradientPipeline
layers={[
{ label: "Layer 1", fn: "a₁ = σ(w₁x)", derivative: 0.6 },
{ label: "Layer 2", fn: "a₂ = σ(w₂a₁)", derivative: 0.5 },
{ label: "Layer 3", fn: "a₃ = σ(w₃a₂)", derivative: 0.5 },
{ label: "Layer 4", fn: "a₄ = σ(w₄a₃)", derivative: 0.4 },
{ label: "Layer 5", fn: "a₅ = σ(w₅a₄)", derivative: 0.3 },
]}
/>Subscribe to stage transitions to drive a sibling chart or transcript:
<GradientPipeline
onStageChange={(stage) => {
/* drive a sibling chart, transcript, etc. */
}}
onReset={() => {
/* clear sibling state */
}}
/>Understanding the component
- Pipeline layout. Each layer is rendered as a rounded box with its label, function, and local derivative
∂. The loss sits to the right as a small badge holding the initial gradient. - Stage machine. Stage
0is idle (pulse at the loss). Each click advances by one — the pulse crosses one layer and the running gradient is updated. The terminal stage shows the summary annotation; one more click resets. - Pulse geometry. The pulse position is the left edge of the layer the pulse has just exited. Its radius, opacity, and colour all track the running gradient divided by the initial gradient. Colour passes through three semantic tokens —
var(--cb-accent)while the ratio is above0.5,var(--cb-warning)between0.2and0.5, andvar(--cb-error)below. - Per-layer labels. Each layer renders two imperatively-driven labels — a multiplication readout below the box and a running-gradient readout above. Both fade in on
SPRINGS.snap, and the running-gradient readout usesSPRINGS.bouncyfor the entry slide. - Box glow + backward arrow. When the pulse crosses a layer, that layer's outer rect flashes via
SPRINGS.dampedand the backward arrow on that span lights up. The first transition also reveals a dashed ghost ring at the pulse's original position so the eye can compare the now-shrunken pulse against where it started. - Reduced motion. Under
prefers-reduced-motion: reduce, all imperative tweens collapse to instant attribute sets, the idle breathing pulse on the loss is suppressed, and stage transitions are immediate.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
layers | readonly { label; fn; derivative }[] | 3-layer canonical demo | Layers ordered left-to-right (Layer 1 leftmost). |
initialGradient | number | 1 | Gradient value at the loss (right-hand side). |
transition | Transition | SPRINGS.smooth | Override the pulse-move transition. |
onStageChange | (stage) => void | — | Fires whenever the stage advances. |
onReset | () => void | — | Fires when the user resets. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The SVG is
role="img"with anaria-labelsummarising the layer count, current stage, and the initial-to-final gradient. - A polite live region announces each stage in plain prose so screen-reader users get the same outcome as sighted users.
- 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 running gradient is also encoded numerically in the per-layer labels and the narration.
- The action button has a context-aware label so the next action is always obvious.
- Motion respects
prefers-reduced-motion: reduce— all imperative tweens collapse to instant attribute sets, and the idle breathing pulse on the loss is suppressed.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/GradientPipeline.tsx). The source was a tightly bundled lesson component — it consumedSvgLabelfrom the SVG primitives andChallengeBtnfrom the lesson chrome, hardcoded the 3-layer set with derivatives0.8 / 0.5 / 0.3and an initial gradient of1, baked in a five-branch narration generator with per-stage button labels, and depended on per-track lesson palette tokens. The viz extract drops the lesson chrome (raw<text>plus a token-styled button), parameteriseslayersandinitialGradientso the component drives any pipeline depth, remaps the colour palette tovar(--cb-*)semantic tokens so consumer themes repaint freely, and re-keys the per-stage springs toSPRINGS.smooth/SPRINGS.snap/SPRINGS.bouncy/SPRINGS.dampedso all motion comes from the same place as every other craft-bits component.