Running Stats Viz
A teaching visualisation for the EMA inside every normalisation layer. As batch means arrive, BatchNorm (and LayerNorm at inference) maintains a running mean and running variance via exponential moving averages:
running_mean = momentum * running_mean + (1 - momentum) * batch_mean
running_var = momentum * running_var + (1 - momentum) * (batch - running_mean)^2
The component plots the incoming batch means as a faint stepped trace, the running mean as a solid accent curve riding it, and the running variance as a ±stddev band around the running mean. The momentum knob makes the responsiveness ↔ stability trade-off visible: high momentum smooths heavily and lags reality; low momentum chases every batch.
Running statistics visualisation.Step 0. Running mean 5.48, stddev 0.
Running statsstep 00/50 μ=5.48 ±0
m=0.90 · σ=0
Customize
EMA
0.90
140ms
Display
Installation
npx shadcn@latest add https://craftbits.dev/r/running-stats-viz.jsonUsage
import { RunningStatsViz } from "@craft-bits/core";
<RunningStatsViz samples={batchMeans} momentum={0.9} defaultPlaying />Drive the cursor externally — pin a specific step with no autoplay:
<RunningStatsViz
samples={batchMeans}
momentum={0.95}
currentStep={28}
playing={false}
/>Hide the variance band for a leaner figure:
<RunningStatsViz samples={batchMeans} showVarianceBand={false} />Anatomy
- Three layers, one chart. The per-batch mean is drawn as a dashed stepped curve in
cb-fg-subtle— the raw signal. The running mean rides on top in solidcb-accent— the EMA. Acb-warning-tinted band hugs the running mean at± sqrt(running_variance)— the second-moment EMA. Together they show the smoothing chain BatchNorm / LayerNorm performs at training time and freezes at inference. - Single-pass precompute. Every render walks
samplesonce and emits two parallel curves of lengthsamples.length + 1. Index 0 is the zero-batch initial state (running mean seeded from the first sample, variance seeded to 0); subsequent indices apply the standard EMA recurrence. The samesamplesalways yield the same curves — safe across SSR and hydration and external scrubber timelines. momentumis the responsiveness knob. Near 1, the EMA listens to the long-run mean and the curve barely budges with each batch — a stable, lagged estimate. Near 0, the EMA chases every batch and the curve becomes nearly identical to the dashed batch-mean trace. The same recurrence underlies BatchNorm'smomentum(PyTorch default0.1→ here0.9) and Adam'sβ1andβ2moment estimates.- Variance band shows uncertainty, not just spread. As batches accumulate, the variance EMA settles around the per-batch noise floor — the band stops shrinking once it captures the actual sampling variation. Under distribution drift the band widens for a few steps before re-anchoring; that is the visual cue for why
track_running_stats=Falsematters in transfer learning. - Autoplay sweeps the cursor. When playing, an interval advances
currentStepby 1 everyplaySpeedms and wraps at the end of the stream. The cursor is a vertical dashed line plus an accent dot pinned to the running-mean curve. Both spring withSPRINGS.snap; the curves themselves spring withSPRINGS.smooth. - Controlled or uncontrolled everywhere.
currentStepandplayingeach have controlled and uncontrolled forms (the Radix pattern). Pass real batch means throughsamplesand the same component works as a scrubbable post-mortem of a training run rather than a synthetic toy. - Reduced motion.
prefers-reduced-motion: reducecollapses every spring to an instant swap and disables autoplay; manual scrubbing still works.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
samples | readonly number[] | — | The stream of batch means. Non-finite entries are dropped. |
momentum | number | 0.9 | EMA decay rate in [0, 1). Clamped to 0.999 so the EMA always moves. |
showVarianceBand | boolean | true | Shade the ±stddev band in cb-warning around the running mean. |
showBatchMean | boolean | true | Draw the per-batch stepped trace under the running mean. |
currentStep | number | — | Controlled cursor step. Pair with onCurrentStepChange. |
defaultCurrentStep | number | 0 | Uncontrolled initial cursor. |
onCurrentStepChange | (step) => void | — | Fires on autoplay tick and scrub. |
playing | boolean | — | Controlled play state. Pair with onPlayingChange. |
defaultPlaying | boolean | true | Uncontrolled initial play state. |
onPlayingChange | (playing) => void | — | Fires when play / pause flips. |
playSpeed | number | 140 | Milliseconds per autoplay tick. |
transition | Transition | SPRINGS.snap | Spring for the cursor. Curves always use SPRINGS.smooth. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The figure is
role="figure"with anaria-labeldescribing the momentum, current step, running mean, and running stddev. - An
aria-live="polite"summary announces the step, running mean, stddev, and current batch whenever the cursor moves. - The play / pause button uses
aria-pressed; its label flips between "Play running stats playback" and "Pause running stats playback". - The running-mean curve is solid, the batch trace is dashed and stepped, and the variance band is a filled polygon — three distinct shape signals, never colour alone.
- The scrubber is a native range input with an explicit
aria-label; arrow keys nudge the cursor by one step and screen readers narrate the value. prefers-reduced-motion: reducecollapses every spring to an instant swap and disables autoplay; manual scrubbing still works.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/nn/RunningStatsViz.tsx). The source was a single-batch-at-a-time number-line interaction with aChallengeBtn"Next Batch" loop, a four-phase narration machine (observe/converging/tracking/insight), an imperativemotion/animatecx loop overrunningDotRef, an SVG glow filter, ghost-dot trails, a true-mean dashed reference, and a phase-coloured narration footer. Re-architected into a curves-over-steps line chart: precomputed running mean (EMA) plus running variance (EMA of squared deviations),cb-warning-tinted ±stddev band, stepped batch-mean trace incb-fg-subtle, and the standardmotion.pathplusSPRINGS.smoothcurve transition pattern. Replaced the--color-accent-*and--color-warn-*raw vars withcb-*tokens, the inlineanimate()loop with declarativemotion.path/motion.circlesprings, and the per-batchChallengeBtnwith the standard autoplay plus scrubber transport. Surfacedsamples,momentum,currentStep, andplayingas controlled / uncontrolled (Radix pattern) props so consumers can pipe in real batch means and own the playback state.