Normalization Viz

A teaching primitive for the dimensions that each normalization scheme reduces over. The same 4D tensor (N, C, H, W) is rendered as a stack of sample cards; switching schemes recolours the cells so that any two cells which share a (mu, sigma) statistic share a hue. The colour map is the lesson — there is no other signal to learn.

BatchNorm    → one (mu, sigma) per channel        across (N, H, W)
LayerNorm    → one (mu, sigma) per sample         across (C, H, W)
GroupNorm    → one (mu, sigma) per (n, group)     across (group_channels, H, W)
InstanceNorm → one (mu, sigma) per (n, channel)   across (H, W)
BatchNorm on a (4, 8, 4, 4) tensor. 8 normalization statistics across (N, H, W) — one statistic per channel.
Normalization(4, 8, 4, 4)
sample 0C=8
sample 1C=8
sample 2C=8
sample 3C=8

BatchNorm across (N, H, W) — one statistic per channel.

Customize
Scheme
batchnorm
Tensor shape
4
8
4
4
GroupNorm
2
Chrome

Installation

npx shadcn@latest add https://craftbits.dev/r/normalization-viz.json

Usage

import { NormalizationViz } from "@craft-bits/core";
 
<NormalizationViz defaultScheme="batchnorm" />

Drive the active scheme from outside the component:

const [scheme, setScheme] = useState<NormalizationScheme>("layernorm");
 
<NormalizationViz scheme={scheme} onSchemeChange={setScheme} />

Pin a specific tensor shape and GroupNorm split:

<NormalizationViz
  defaultScheme="groupnorm"
  tensorShape={{ batch: 2, channels: 6, height: 3, width: 3 }}
  numGroups={3}
/>

Hide the picker for a fully-controlled embed (e.g. an animated narration):

<NormalizationViz scheme="instancenorm" showSchemePicker={false} />

Anatomy

  1. Cells, not numbers. The component renders empty cells — no activation values, no statistics readouts. The point of this primitive is the grouping, not the maths. Cells that share a colour share a (mu, sigma) statistic; that's the entire signal.
  2. Stack of samples. The tensor is laid out as one card per n ∈ [0, batch). Each card contains C rows, one per channel. Each row is an H × W mini-grid. The spatial order is preserved across scheme switches so the eye can track a single cell through every scheme.
  3. Six-hue OKLCH palette. Group ids are hashed against (220, 160, 30, 320, 270, 90) — six perceptually-spaced hues. The cycle is long enough to disambiguate common shapes; when group counts exceed six (e.g. InstanceNorm on a large tensor) the structured group-id ordering still keeps adjacent groups visually distinct.
  4. GroupNorm divisor clamping. If numGroups does not divide channels, the component walks numGroups downward to the next valid divisor so every group has the same width. The fallback is always numGroups=1 (LayerNorm-on-the-channel-axis), which divides any positive channels.
  5. SPRINGS.smooth on every cell. Switching schemes animates backgroundColor and borderColor on each cell with SPRINGS.smooth from @craft-bits/core/motion. The animation is the bridge that lets the learner see which cells regrouped. prefers-reduced-motion: reduce collapses every transition to an instant swap.
  6. Controlled + uncontrolled. scheme follows the Radix value / defaultValue pattern: pass scheme + onSchemeChange to drive externally, or defaultScheme to let the component own its own state. The picker is a role="radiogroup"; pass showSchemePicker={false} for a chrome-free embed.

Props

PropTypeDefaultDescription
schemeNormalizationSchemeControlled active scheme. Pair with onSchemeChange.
defaultSchemeNormalizationScheme"batchnorm"Uncontrolled initial scheme.
onSchemeChange(scheme) => voidFires when the scheme changes.
tensorShapePartial<NormalizationTensorShape>{ batch: 4, channels: 8, height: 4, width: 4 }4D shape (N, C, H, W). Each dim clamped to a legible range.
numGroupsnumber2Number of GroupNorm groups. Clamped to the next divisor of channels.
showSchemePickerbooleantrueShow the segmented scheme picker.
showCaptionbooleantrueShow the footer caption explaining the active scheme.
transitionTransitionSPRINGS.smoothSpring for cell colour transitions.
classNamestringMerged onto the root <div> via cn().

Accessibility

  • The root is role="figure" with aria-labelledby naming the chart "Normalization" and an aria-live="polite" summary announcing the active scheme, tensor shape, number of statistics, and reduction dimensions whenever the scheme changes.
  • The scheme picker is a role="radiogroup" of role="radio" buttons with aria-checked reflecting the active scheme — keyboard users tab into the group, and screen readers narrate the selection.
  • The tensor is a role="list" of role="listitem" sample cards; each channel row carries an aria-label of the form Channel n=X, c=Y, group Z so assistive tech can navigate one channel at a time.
  • Group identity is signalled by colour and the channel-row label includes the group id — no information depends on colour alone.
  • prefers-reduced-motion: reduce collapses every cell transition to an instant swap.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/viz/NormalizationViz.tsx). The source coupled a ModeStrip explore / predict toggle, a 6-round usePredictRounds quiz, a LabeledSlider pair for the learnable γ / β affine parameters, a deterministic 8-element bar histogram with live μ, σ readouts, ChallengeBtn / TogglePill / FeedbackBadge / ScoreDots / DoneCard chrome, and a per-sample noise sampler — all centred on the mathematics of one-dimensional normalization. The library version drops the predict mode, the bar chart, the affine sliders, the narration heuristics, and every piece of source-project chrome. Reframed as the primitive every modern-normalization lesson needs: the dimensions view — a stack of sample cards whose cells colour by (mu, sigma) group under each of BatchNorm / LayerNorm / GroupNorm / InstanceNorm. Replaced the raw --color-accent-* / --color-ink-* palette with six-hue OKLCH groups; switched to --cb-* tokens for chrome; clamped (channels, numGroups) to uniform divisors; exposed Radix controlled / uncontrolled scheme; added a role="radiogroup" picker with aria-checked; and gated every transition on prefers-reduced-motion. Sits in ML Viz → Regularization alongside OverfittingGapViz, RunningStatsViz, VarianceCompoundViz, L2WeightDecayViz, and DropoutToggle.