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.jsonUsage
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
- Temperature-scaled softmax. Each step computes
p_i = exp(z_i / T) / Σ exp(z_j / T)over the supplied logits.Tis clamped at1e-6to dodge division-by-zero; if the sum collapses to zero orInfinity, every token gets1 / Nso the bars stay finite. - One click, one sample.
Sample next tokencalls the configuredsampler(defaults toMath.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-outm.div, and its row label colours over tovar(--cb-accent)so the eye lands on it without needing to read the strip. - Bars are live, history is a transcript. The bar widths spring toward the new probabilities (
SPRINGS.default) every timeTmoves; the history strip is append-only — older samples don't redraw, only newer ones animate in viamode="popLayout". - Frequency counter. A
×Nbadge 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. - 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
| Prop | Type | Default | Description |
|---|---|---|---|
tokens | readonly TemperatureSamplerInlineToken[] | 8 tokens | Token bank. Each entry contributes one bar. |
initialTemperature | number | 1 | Starting temperature (clamped into tempRange). |
tempRange | readonly [number, number] | [0.1, 3] | Slider extents. Both must be > 0 and min < max. |
tempStep | number | 0.1 | Slider step granularity. |
historyLimit | number | 30 | Max samples kept in the history strip. |
caption | ReactNode | — | Optional caption rendered beneath the strip. |
transition | Transition | SPRINGS.default | Override the spring for bar / history transitions. |
sampler | () => number | Math.random | Override the underlying RNG (seeded demos, tests). |
onSample | (index: number) => void | — | Fires whenever a new sample lands. |
onTemperatureChange | (next: number) => void | — | Fires whenever the temperature changes. |
className | string | — | Merged 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/Endall 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 afocus-visiblering 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, andmode="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 aLabeledSliderchrome wrapper from the project'scomponents/uifor 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 inlineSPRINGS.snugreference 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 tovar(--cb-accent)/var(--cb-fg)/var(--cb-bg-elevated), and remapsSPRINGS.snugtoSPRINGS.defaultfrom@craft-bits/core/motion. Thesamplerinjection point,onSample/onTemperatureChangecallbacks,tempRange/tempStep/historyLimitknobs, and theusePrefersReducedMotionshort-circuit are new — the source rolledMath.randomin hard, threw out samples on every render, and had no reduced-motion path.