Variance Compound Viz
A bar chart of activation variance per layer for a deep feed-forward network. Each bar is Var(h_l) = inputVariance · varianceFactor^l — the textbook geometric-series approximation of how the second moment of activations evolves with depth under a constant per-layer multiplier. Drag the varianceFactor slider and watch the bars collapse into the vanishing regime or blow past the top into the exploding one. The component exists to make the case for Xavier and He initialisation visceral: when the per-layer factor isn't exactly 1, depth amplifies any mismatch exponentially.
Installation
npx shadcn@latest add https://craftbits.dev/r/variance-compound-viz.jsonUsage
import { VarianceCompoundViz } from "@craft-bits/core";
<VarianceCompoundViz />Pick a different depth and the input variance the initialiser is preserving:
<VarianceCompoundViz numLayers={32} inputVariance={1.0} />Drive varianceFactor from outside (controlled mode):
const [factor, setFactor] = useState(1.0);
<VarianceCompoundViz
varianceFactor={factor}
onVarianceFactorChange={setFactor}
/>Switch to a linear y-axis to dramatise the collapse or the blow-up:
<VarianceCompoundViz showLogScale={false} defaultVarianceFactor={0.7} />Understanding the component
- The formula on the bars. Each bar shows
Var(h_l) = inputVariance · varianceFactor^l. Layer0(the input) is on the left; layernumLayers − 1(the deepest layer) is on the right. The forward direction matches the way signal flows through the network, so the bars on the right tell you what arrives at the final layer. - Log scale is the default for a reason. At depth 16 with
varianceFactor = 1.2, deep-layer variance reaches~19; at0.8it falls to~0.03. Linear axes clip one or the other; the log axis centres oninputVarianceand walks 6 decades above and 6 below, so the dramatic case is always visible without losing the stable one. - Baseline is the input variance. The midline marks
Var(h_0)— the variance the initialiser is trying to preserve. Bars above the midline mean variance has grown; bars below mean it has shrunk. AtvarianceFactor = 1.0every bar sits exactly on the midline — the "stable" pose Xavier and He target. - Regime colors are derived, not state. When the per-layer factor exceeds
1.05and the deepest bar is>10×the input variance, bars paint--cb-error(exploding). When the factor is below0.95and the deepest bar is<0.1×the input, bars paint--cb-warning(vanishing). Otherwise everything is--cb-accent(stable). The regime label under the slider gives the textual diagnosis — colour is never the only signal. SPRINGS.smoothon the bar heights. Dragging the slider springs each bar'sheightandyindependently — the animation reads as a wave that ripples across the chart.prefers-reduced-motion: reducecollapses every spring to instant.- The Xavier / He connection. For identity / tanh, Xavier sets weight variance
1 / fan_inso the per-layer variance multiplier is exactly1. For ReLU, He picks2 / fan_into cancel the activation's0.5second-moment factor and again land at1. Get the constant wrong and the chart says so in one glance — that's the entire teaching purpose.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
numLayers | number | 16 | Network depth — one bar per layer. Clamped to [2, 64]. |
inputVariance | number | 1.0 | Variance entering layer 0 — the baseline the initialiser tries to preserve. |
varianceFactor | number | — | Controlled per-layer variance multiplier. Pair with onVarianceFactorChange. |
defaultVarianceFactor | number | 1.0 | Uncontrolled initial per-layer multiplier. |
onVarianceFactorChange | (factor) => void | — | Fires when the user drags the slider. |
showLogScale | boolean | true | Plot the y-axis on a log10 scale centred on inputVariance. |
transition | Transition | SPRINGS.smooth | Spring for bar-height transitions. |
className | string | — | Merged onto the root <div> via cn(). |
Accessibility
- The figure is
role="figure"with anaria-labelledbythat names the chart "Variance per layer." - An
aria-live="polite"summary announces the input variance, the per-layer factor, the final deep-layer variance, and the diagnosed regime (stable/vanishing/exploding) whenever any input changes. - The slider is a
LabeledSlider, itself a native<input type="range">— keyboard arrows nudge the factor,Home/Endjump to extremes, and screen readers narrate the value. - Color is never the only signal — the regime label and the final-variance readout are both textual.
prefers-reduced-motion: reducesnaps every bar transition; no per-step easing is shown.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/nn/VarianceCompoundViz.tsx). The source was a 6-layer fixed-depth widget driven by aσ(weight standard deviation) slider withD = 5baked in, a four-phase narration state machine (observe/exploding/vanishing/balanced), a1/√Dcritical-sigma marker on the slider track, imperativemotion.animate()on each bar'sheightand label content, a breathing-pulse hint on the rightmost bar, avar=1baseline reference line, andSvgLabelchrome. Reframed as a depth-agnostic primitive —varianceFactordirectly is the per-layer multiplier (so consumers can express Xavier as1.0, He as the activation-cancelling value, or any custom scheme) andinputVarianceis the baselineVar(h_0). Stripped the phase / narration system, the1/√Dtrack marker, the SVG-glow filter, the imperative animation loop, and the breathing pulse. Replaced the raw<input type="range">withLabeledSlider, switched bar colour and y-axis scaling to derived regime / log-around-baseline so the chart works at anyinputVariance, and added Radix controlled / uncontrolled pairs forvarianceFactor. Sits in ML Viz → Regularization alongsideDropoutEnsembleViz,OverfittingGapViz, andRunningStatsViz.