Temperature Sampler Inline

An inline primitive for prose flow that makes temperature sampling tactile. Each row is a token; the bar is its current probability under softmax(z / T). Click Sample next token to draw one — the token's bar flashes, the per-row frequency counter ticks, and the sampled word is pushed onto the history strip. Scrub temperature to feel low-T collapse to argmax and high-T fan out toward uniform.

Temperature sampler with 8 tokens.
the47.1%
a12.8%
cat25.8%
dog7.0%
sat4.7%
ran1.6%
jumped0.6%
slept0.3%
1.0
Customize
Distribution
prose
1.00
History
30

Installation

npx shadcn@latest add https://craftbits.dev/r/temperature-sampler-inline.json

Usage

import { TemperatureSamplerInline } from "@craft-bits/viz/temperature-sampler-inline";
 
<TemperatureSamplerInline />

Provide a custom token bank and a starting temperature:

<TemperatureSamplerInline
  tokens={[
    { word: "the", logit: 3.1 },
    { word: "a", logit: 1.8 },
    { word: "cat", logit: 2.5 },
    { word: "dog", logit: 1.2 },
  ]}
  initialTemperature={0.5}
  caption="At T = 0.5 the cat / the dominate."
/>

Seed a deterministic sampler for tests or stable demos:

import seedrandom from "seedrandom";
 
const rng = seedrandom("demo-seed");
 
<TemperatureSamplerInline
  sampler={() => rng()}
  onSample={(i) => console.log("drew token", i)}
/>

Understanding the component

  1. Temperature-scaled softmax. Each step computes p_i = exp(z_i / T) / Σ exp(z_j / T) over the supplied logits. T is clamped at 1e-6 to dodge division-by-zero; if the sum collapses to zero or Infinity, every token gets 1 / N so the bars stay finite.
  2. One click, one sample. Sample next token calls the configured sampler (defaults to Math.random), runs a cumulative-distribution walk over the current probabilities, and pushes the chosen index onto the history. The chosen bar flashes via a layered fade-out m.div, and its row label colours over to var(--cb-accent) so the eye lands on it without needing to read the strip.
  3. Bars are live, history is a transcript. The bar widths spring toward the new probabilities (SPRINGS.default) every time T moves; the history strip is append-only — older samples don't redraw, only newer ones animate in via mode="popLayout".
  4. Frequency counter. A ×N badge appears next to each row once any samples land, so the learner can compare the live probability against the empirical frequency and watch one converge to the other.
  5. Slider clears history. Changing temperature resets the sample strip — the empirical distribution stops being meaningful when its underlying distribution just moved, so we throw the stale samples away to keep the readout honest.

Props

PropTypeDefaultDescription
tokensreadonly TemperatureSamplerInlineToken[]8 tokensToken bank. Each entry contributes one bar.
initialTemperaturenumber1Starting temperature (clamped into tempRange).
tempRangereadonly [number, number][0.1, 3]Slider extents. Both must be > 0 and min < max.
tempStepnumber0.1Slider step granularity.
historyLimitnumber30Max samples kept in the history strip.
captionReactNodeOptional caption rendered beneath the strip.
transitionTransitionSPRINGS.defaultOverride the spring for bar / history transitions.
sampler() => numberMath.randomOverride the underlying RNG (seeded demos, tests).
onSample(index: number) => voidFires whenever a new sample lands.
onTemperatureChange(next: number) => voidFires whenever the temperature changes.
classNamestringMerged onto the root via cn().

Accessibility

  • Each row is announced as a list item with the token glyph, its probability, and its empirical sample count, so screen-reader users get the same readout as sighted users.
  • The temperature input is a native <input type="range">Arrow, Page Up / Page Down, Home / End all work out of the box and the value is mirrored alongside via a tabular-nums readout.
  • The action buttons clear the ≥ 32 × 32 px minimum hit area (min-h-[2.25rem]) and announce keyboard focus through a focus-visible ring drawn off --cb-accent.
  • The history strip is a polite live region (aria-live="polite", aria-label="Sampled tokens history") so each new draw gets read aloud, and mode="popLayout" keeps stable keys when older samples fall off the front.
  • Motion respects prefers-reduced-motion: reduce — bar springs, the flash on the active row, the history strip enter / exit, and the per-chip pop all collapse to a single snap.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/viz/TemperatureSamplerInline.tsx). The source used a LabeledSlider chrome wrapper from the project's components/ui for the temperature input, hand-rolled lesson palette tokens (--color-accent-300/400/500, --color-ink-200/400/500, --color-surface-elevated, --color-bar-*) for the bar / button / chip colours, and an inline SPRINGS.snug reference that does not exist in the canonical motion set. The viz extract drops every project-specific dependency — it uses a token-styled native <input type="range">, swaps the palette over to var(--cb-accent) / var(--cb-fg) / var(--cb-bg-elevated), and remaps SPRINGS.snug to SPRINGS.default from @craft-bits/core/motion. The sampler injection point, onSample / onTemperatureChange callbacks, tempRange / tempStep / historyLimit knobs, and the usePrefersReducedMotion short-circuit are new — the source rolled Math.random in hard, threw out samples on every render, and had no reduced-motion path.