Softmax Temperature Viz

Bar chart of temperature-scaled softmax with two modes. Explore lets the learner drag each bar vertically to change its logit, scrub the temperature slider, and toggle the max trick (subtract max(z / T) before exp) to watch exp() actually overflow when the trick is off. Predict runs six MCQ rounds on softmax intuition — temperature halving, shift-invariance, argmax limit at extreme T, sign flip, uniform input, near-zero T — and morphs the chart from question state to the reveal state so the answer is visible alongside the explanation.

Softmax temperature visualisation with 6 predict rounds.
02.0cat1.00exp0.59prob1.0dog0.37exp0.22prob0.3fox0.18exp0.11prob-0.5fish0.08exp0.05prob-1.0bird0.05exp0.03prob

cat leads. Drag bars to change logits, slide temperature to control how decisive the mapping is.

1.00
Customize
Distribution
skewed
1.00
Stability
Mode
explore

Installation

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

Usage

import { SoftmaxTemperatureViz } from "@craft-bits/viz/softmax-temperature-viz";
 
<SoftmaxTemperatureViz />

Override the initial logits, palette, and starting mode:

<SoftmaxTemperatureViz
  initialLogits={[3.0, 1.5, 0.5, 0.0, -0.5]}
  labels={["cat", "dog", "fox", "fish", "bird"]}
  defaultMode="predict"
  onPredictComplete={({ correct, total }) => {
    console.log(`${correct} / ${total}`);
  }}
/>

Drop in a custom predict bank:

<SoftmaxTemperatureViz
  predictRounds={[
    {
      q: "Does doubling T make the distribution flatter or sharper?",
      opts: ["Sharper", "Flatter"],
      answer: -1,
      logits: [2, 1, 0, -1],
      T: 1,
      revealT: 2,
      maxTrick: true,
      reveal: "Higher T compresses the gap between scaled logits, flattening the distribution toward uniform.",
    },
  ]}
/>

Understanding the component

  1. Temperature-scaled softmax. Each bar is exp(z_i / T) normalised by the sum of all exponentials. Lowering T amplifies the gap between logits before normalisation — the highest one wins outright. Raising T compresses gaps, so every class limits to 1 / N.
  2. Draggable bars. In explore mode each bar carries role="slider". Drag vertically with the pointer, or Tab to a bar and use the arrow keys (Shift steps by 1.0, plain steps by 0.1) to change its logit. The chart animates with SPRINGS.snap on every nudge; reduced-motion users snap instantly.
  3. Max-subtraction trick. When ON, the implementation subtracts the per-row maximum scaled logit before exp (the log-sum-exp trick). When OFF, large logits or tiny T push the exponential past the float64 limit and produce Infinity — the exp row turns red and an overflow callout fades in.
  4. Per-bar readout. Below each bar: the exp value, the probability, and a thin probability fill bar so the user can compare relative scale even when the largest bar saturates the chart.
  5. Predict mode. Six canned rounds test softmax intuition. The chart shows the question's logit/T state while the learner picks an option; after Check, the chart morphs (when revealLogits or revealT is set) so the answer is visible, not just told. A ScoreDots row tracks per-round correctness and DoneCard summarises the run.
  6. Narration. A polite live region carries a state-aware narration: which class dominates, whether the distribution is near-uniform or near-argmax, and a specific overflow hint when the max trick is off and exp() has actually blown up.

Props

PropTypeDefaultDescription
initialLogitsreadonly number[][2, 1, 0.3, -0.5, -1]Initial logits for explore mode.
labelsreadonly string[]z₁..z_NPer-bar labels.
colorsreadonly string[]accent/info/success/warning/errorCSS colour strings cycled across bars.
initialTemperaturenumber1Initial T for explore mode.
initialMaxTrickbooleantrueInitial max-trick state for explore mode.
zMinnumber-5Lower bound on draggable logit range.
zMaxnumber5Upper bound on draggable logit range.
tempRangereadonly [number, number][0.1, 5]Slider extents.
predictRoundsreadonly SoftmaxTemperatureVizPredictRound[]6 roundsOverride the predict-mode question bank.
defaultModeSoftmaxTemperatureVizMode"explore"Mode visible on first render.
transitionTransitionSPRINGS.snapOverride the spring for bar transitions.
onModeChange(mode) => voidFires when the active mode changes.
onPredictComplete(score) => voidFires when the learner finishes all predict rounds.
classNamestringMerged onto the root via cn().

Accessibility

  • The mode strip is a role="group" with aria-pressed on each ModeButton, so screen-reader users can announce and switch modes via keyboard.
  • Every bar is a role="slider" with aria-valuemin / aria-valuemax / aria-valuenow and an aria-label of "z₁: logit 2.0, probability 0.50" so colour is never the only signal.
  • Keyboard: bars are reachable via Tab; / nudge by 0.1, / nudge by −0.1, hold Shift to step by 1.0. The temperature slider is a native <input type="range">.
  • A polite live region carries the narration so assistive-tech users hear which class dominates, when the distribution is near-uniform, and when overflow has occurred.
  • Option buttons in predict mode expose aria-pressed for selection and use a coloured border + background alongside a FeedbackBadge so correctness is never colour-only.
  • Every interactive control has a ≥ 32×32px hit area.
  • Motion respects prefers-reduced-motion: reduce.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/viz/SoftmaxTemperatureViz.tsx). The source was a lesson primitive that consumed ModeStrip / ChallengeBtn / FeedbackBadge / ScoreDots / DoneCard / usePredictRounds from the lesson's ConstructionPrimitives module, TogglePill and LabeledSlider chrome from components/ui, an SvgLabel primitive, and palette tokens like --color-bar-1..5 / --color-success-500 / --color-fail-500 / --color-ink-200. The viz extract drops every project-specific dependency — it inlines token-styled ModeButton / OptionButton / PrimaryButton / SecondaryButton / FeedbackBadge / ScoreDots / DoneCard and a native <input type="range"> so the component has no source-project imports. The inline { type: "spring", stiffness: 340, damping: 28 } is replaced by SPRINGS.snap from @craft-bits/core/motion; the per-bar palette is remapped to a five-step default cycle of --cb-accent / --cb-info / --cb-success / --cb-warning / --cb-error with a colors prop override. Hard-coded narration is preserved but lessonId / phaseId / cardIndex props are stripped, and the question bank is exposed as predictRounds so consumers can swap it. onModeChange and onPredictComplete callbacks lift score state out of the component.