GPU Timeline Profiler
A Gantt-chart view of a GPU trace. Each row is a stream — the compute queue, the copy engine, a synchronisation lane — and each rectangular block is one executable unit (a kernel launch, a memory transfer, an event record). An optional vertical cursor at currentTime threads the chart through external playback, scrubbing, or narration state.
GPU timeline. 3 streams, 8 events, trace duration 22.00 ms. Cursor at t=8.50 ms. compute: 4 events; copy: 2 events; sync: 2 events.
GPU timelinet=8.50 / 22.00 ms
Customize
Timeline
8.5ms
22px
Installation
npx shadcn@latest add https://craftbits.dev/r/gpu-timeline-profiler.jsonUsage
import { GPUTimelineProfiler } from "@craft-bits/core";
const streams = [
{
id: "compute",
label: "compute",
events: [
{ id: "qkv", start: 1.0, duration: 3.4, label: "qkv" },
{ id: "attn", start: 4.6, duration: 5.8, label: "attn", tone: "success" },
{ id: "proj", start: 10.6, duration: 2.6, label: "proj" },
{ id: "ffn", start: 13.4, duration: 6.2, label: "ffn" },
],
},
{
id: "copy",
label: "copy",
events: [
{ id: "h2d", start: 0.0, duration: 1.4, label: "h2d" },
{ id: "d2h", start: 19.8, duration: 1.6, label: "d2h" },
],
},
{
id: "sync",
label: "sync",
events: [
{ id: "evt", start: 10.4, duration: 0.4, label: "evt", tone: "warning" },
{ id: "syn", start: 21.4, duration: 0.6, label: "sync", tone: "warning" },
],
},
];
<GPUTimelineProfiler streams={streams} currentTime={8.5} />Drive the cursor from outside (scrubbing, narration, MDX prose hover):
<GPUTimelineProfiler streams={streams} currentTime={t} />Stretch the horizontal axis for sub-millisecond traces:
<GPUTimelineProfiler streams={streams} unitsPerMs={120} />Understanding the component
- One row per stream. Streams render in the order they appear in
streams— the component never re-sorts. The row label sits in the left gutter; the lane background is a single rounded rectangle so empty time is still visually anchored. - Blocks placed by
start+duration. Each event becomes a rounded rectangle whosexisstart * effectivePxPerMsand whosewidthisduration * effectivePxPerMs. Tiny blocks (under 2 px) widen to a 2 px floor so single-tick events stay visible. - Tone via semantic tokens.
accent(default),warning,success,errormap to--cb-accent,--cb-warning,--cb-success,--cb-error— recolour your theme and the timeline repaints. Colour is not the only signal: every block can carry alabeland the row has its own gutter label. - Effective
pxPerMsfloors at a legible chart.unitsPerMsis the requested horizontal scale, but the chart floors at 320 px wide so a short trace at the defaultunitsPerMs: 1still reads. The effective scale ischartWidth / traceDurationso blocks fill the lane instead of clustering on the left. - Blocks enter on a stagger. Each block fades and
scaleYs in from0.6onSPRINGS.snapwith a per-index delay capped at 0.25 s — the trace "lights up" left to right when it mounts, then sits still. - Optional vertical cursor. When
currentTimeis supplied and falls inside[0, traceDuration], a dashed accent-toned line draws at that x. The cursor animates viatransform: translateX, so scrubbing is GPU-composited. - Reduced motion.
prefers-reduced-motion: reducecollapses every spring to{ duration: 0 }, skips the enter stagger entirely, and renders the final frame on first paint.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
streams | readonly GPUTimelineStream[] | — | Required. One row per item, rendered in the supplied order. |
currentTime | number | — | Optional cursor position on the shared time axis. |
unitsPerMs | number | 1 | Requested horizontal scale, in pixels per millisecond. |
transition | Transition | SPRINGS.snap | Spring used for block enter and cursor transitions. |
className | string | — | Merged onto the root <div> via cn(). |
GPUTimelineStream
| Field | Type | Description |
|---|---|---|
id | string | Unique key — used as the React key and the a11y token. |
label | string | Row label drawn in the left gutter. |
events | readonly GPUTimelineEvent[] | Events to draw on this stream's row. |
GPUTimelineEvent
| Field | Type | Description |
|---|---|---|
id | string | Unique within the parent stream — used as the React key. |
start | number | Start time on the shared axis, in caller units. |
duration | number | Duration in the same units. Floored to 1 px at render. |
label | string? | Drawn inside the block when it fits (>= 36 px wide). |
tone | "accent" | "warning" | "success" | "error"? | Semantic tone. Defaults to "accent". |
Accessibility
- The root is
role="figure"witharia-labelledbypointing at the "GPU timeline" heading andaria-describedbyat a visually-hiddenaria-live="polite"summary that narrates stream count, event count, trace duration, and cursor position. - Stream labels live as SVG
<text>inside the chart and as part of the live summary, so screen-reader users get the row structure even though the chart itself isrole="presentation". - Tone is reinforced by the optional in-block
labeland the gutter row label, so colour is never the only signal — error and warning blocks read as distinct without the contrast. - Motion respects
prefers-reduced-motion: reduce: blocks paint without the enter stagger and the cursor moves without a spring.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/GPUTimelineProfiler.tsx). The original was a three-preset (tiny / compute-bound / fused) teaching widget with an internal stage machine, an Auto-play / Next-stage controller, a fixed CPU/GPU/Transfer lane model, a side-by-side "5-dispatch vs fused" comparison row, ad-hoc summary metrics (Total / Compute % / Preset), and narration panels reading from a stage'snarrationfield. The library extract is the pure Gantt primitive — streams in, blocks out, optional cursor through — so callers compose the presets, the comparison, the summary, and the narration around it. Sibling toRooflinePlotter,KVCacheViz,MemoryBudgetAllocatorunder ML Viz → Performance.