Thread Trace Panel
Collapsible diagnostic table for parallel-tile algorithms. One row per iteration, one column per traced scalar (m_i, l_i, α, row_max, row_sum, …). Pick a Q-block / thread pair from two row-of-pills selectors and the panel renders that cell's trace against a reference, colouring each value green or red by whether it falls within tolerance of the expected value.
The visualisation comes from Flash Attention debugging: when an online softmax recurrence diverges from its closed-form reference, knowing which iteration, which thread, and which scalar broke is the whole game.
Thread trace — per-iteration scalar table for one Q-block / thread pair.
Q Block
Thread
| iter | m_i | l_i | α | row_max | row_sum |
|---|---|---|---|---|---|
| 0 | -∞ | 0.0000 | 1.0000 | 2.3412 | 18.4291 |
| 1 | 2.3412 | 18.4291 | 0.6321 | 2.8107 | 33.0518 |
| 2 | 2.8107 | 33.0518 | 0.8912 | 2.9261 | 56.2243 |
| 3 | 2.9261 | 56.2243 | 0.9704 | 2.9563 | 74.8830 |
All values match reference within 1e-4 tolerance.
Customize
Trace
1e-4
40 ms
Installation
npx shadcn@latest add https://craftbits.dev/r/thread-trace-panel.jsonUsage
import { ThreadTracePanel } from "@craft-bits/viz/thread-trace-panel";
<ThreadTracePanel
getRowsFor={(qBlock, thread) =>
qBlock === 0 && thread === 0
? { rows: myActualTrace, reference: myReferenceTrace }
: null
}
/>;Drive open / selection from outside (parent scrubber, lesson narration):
const [open, setOpen] = useState(false);
const [qBlock, setQBlock] = useState(0);
const [thread, setThread] = useState(0);
<ThreadTracePanel
open={open}
onOpenChange={setOpen}
qBlock={qBlock}
onQBlockChange={setQBlock}
thread={thread}
onThreadChange={setThread}
rows={myRowsForCurrentCell}
referenceRows={myReferenceForCurrentCell}
/>;Custom columns for a different recurrence:
<ThreadTracePanel
columns={[
{ key: "step", label: "step", decimals: 0, isLabel: true },
{ key: "loss", label: "loss", decimals: 6 },
{ key: "grad_norm", label: "‖∇‖", decimals: 4 },
]}
rows={steps}
referenceRows={referenceSteps}
tolerance={1e-3}
/>Understanding the component
- Two selectors, one cell. The Q-block and thread row-of-pills selectors pick a
(qBlock, thread)pair. Either passrows+referenceRowsdirectly, or provide agetRowsFor(qBlock, thread)callback the component invokes on every selector change. - Cell-level match check. For every non-label column, the actual value is compared against the reference using
|actual − reference| < tolerance. Both+∞and−∞are treated as exact-match. Cells render their value coloured by the result —var(--cb-success)when matching,var(--cb-error)when diverging,var(--cb-fg-subtle)for label columns. - Phase machine. The root carries a
data-phaseattribute —collapsed,missing,matching, ordiverging. Useful for styling hooks in surrounding UI. - Row entrance stagger. Rows enter with a 4 ms / row stagger (clamped to
[0, 50 ms]viarowStaggerSec) so the trace reads as a coordinated reveal. Reduced-motion users get the table flat and instant. - Reduced motion. Under
prefers-reduced-motion: reduce, the collapse animation, the selector pill colour transition, and the row entrance stagger all collapse to instant. - Controlled + uncontrolled, three ways.
open,qBlock, andthreadeach accept a controlled prop paired withon*Change, or matchingdefault*uncontrolled variants. - Narration overrides. Pass
matchNarration/divergeNarration/emptyNarrationto rewrite the three states' callouts.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rows | readonly ThreadTracePanelRow[] | null | — | Rows for the current cell. null triggers the empty callout. |
referenceRows | readonly ThreadTracePanelRow[] | null | rows | Reference rows. Defaults to rows. |
getRowsFor | (qBlock, thread) => { rows, reference? } | null | — | Lookup called on every selector change. |
columns | readonly ThreadTracePanelColumn[] | iter / m_i / l_i / α / row_max / row_sum | Column definitions in render order. |
qBlocks | readonly number[] | [0, 1, 2, 3] | Q-block selector options. |
threads | readonly number[] | [0, 1, 2, 3] | Thread selector options. |
qBlock | number | — | Controlled selected Q-block. |
defaultQBlock | number | 0 | Uncontrolled initial Q-block. |
onQBlockChange | (qBlock) => void | — | Fires when the Q-block changes. |
thread | number | — | Controlled selected thread. |
defaultThread | number | 0 | Uncontrolled initial thread. |
onThreadChange | (thread) => void | — | Fires when the thread changes. |
open | boolean | — | Controlled open state. |
defaultOpen | boolean | false | Uncontrolled initial open state. |
onOpenChange | (open) => void | — | Fires when the panel opens / closes. |
tolerance | number | 1e-4 | Absolute tolerance for the cell-level match check. |
transition | Transition | SPRINGS.snap | Override the collapse + row entrance spring. |
rowStaggerSec | number | 0.04 | Per-row entrance stagger, clamped to [0, 0.05]. |
formatValue | (value, column) => string | toFixed(decimals) | Override the cell formatter. |
toggleLabel | (open) => ReactNode | "Show / Hide thread trace" | Override the toggle button label. |
matchNarration | ReactNode | tolerance copy | Override the success narration. |
divergeNarration | ReactNode | rescale-hint copy | Override the failure narration. |
emptyNarration | ReactNode | "No reference data …" | Override the missing-cell callout. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The root carries a hidden title via
aria-labelledbysummarising the panel's scope. - The toggle button is
aria-expanded+aria-controls-linked to the collapsible region. - Each selector row is a
role="radiogroup"witharia-label; each pill isrole="radio"witharia-checked. Colour is paired withdata-state, focus rings, and an integer label. - The three narration callouts are
aria-live="polite"regions — the missing / matching / diverging copy is announced as the trace updates. - Cells are
tabular-numsand consistently padded. - Motion respects
prefers-reduced-motion: reduce— the collapse, the pill colour transition, and the row stagger all collapse to instant.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/ThreadTracePanel.tsx). The source paired a hard-coded Q-block 0 / thread 0 reference trace with a lesson-coupledvisibleprop driven from a parentlessonIdscrubber, aTogglePillimport for the selectors, an inlinevar(--color-accent-400)/var(--color-success-400)/var(--color-fail-500)palette, and aSPRINGS.snappyreference that doesn't exist on the library's motion module. The viz extract strips every lesson-only import, remaps every inline colour to the--cb-accent/--cb-success/--cb-error/--cb-warningtoken vocabulary, swapsSPRINGS.snappyfor the canonicalSPRINGS.snap, generalises the four hard-coded columns into acolumnsprop and the single reference dictionary into agetRowsForcallback, and exposes controlled+uncontrolledopen/qBlock/threadprops so the panel can be driven by an outer scrubber or by its own selectors.