Memory Stack Viz
A tall narrow vertical bar that plots a contiguous address range. Each region carries an explicit sizeMb and an optional tone, and is labelled with its size plus start/end offset in hex. The first region starts at 0x0 at the bottom of the stack; each successive region starts where the previous one ended. Optional totalCapacityMb introduces a trailing empty headroom slice and a top-of-stack address so the unused gap is read by length.
Unlike CapacityStack — which buckets the GPU memory budget of a single training step against device capacity — MemoryStackViz draws the actual layout of a contiguous address range so the practitioner can point at any byte and say "that one lives here."
- .text (code)4.00 MB0x0000_00000x0040_0000
- .rodata2.00 MB0x0040_00000x0060_0000
- .data6.00 MB0x0060_00000x00C0_0000
- .bss8.00 MB0x00C0_00000x0140_0000
- heap96.0 MB0x0140_00000x0740_0000
- stack8.00 MB0x0740_00000x07C0_0000
- free132 MB
Installation
npx shadcn@latest add https://craftbits.dev/r/memory-stack-viz.jsonUsage
import { MemoryStackViz } from "@craft-bits/core";
<MemoryStackViz
regions={[
{ id: "text", label: ".text (code)", sizeMb: 4, tone: "accent" },
{ id: "data", label: ".data", sizeMb: 6, tone: "warning" },
{ id: "heap", label: "heap", sizeMb: 96, tone: "success" },
{ id: "stack", label: "stack", sizeMb: 8, tone: "error" },
]}
totalCapacityMb={256}
/>Hide the hex offsets when only the rough composition matters:
<MemoryStackViz regions={regions} totalCapacityMb={256} showOffsets={false} />Drive the cursor from outside the component:
const [current, setCurrent] = useState<string | null>("heap");
<MemoryStackViz
regions={regions}
totalCapacityMb={256}
currentRegion={current}
onCurrentRegionChange={setCurrent}
/>Understanding the component
- One track, many regions. Each region is a child
motion.buttonwhoseheightanimates to its share of the track. The track itself is a single rounded box withflex-col-reverse, so the first region inregionssits at the bottom (lowest address). totalCapacityMbsizes the address range. When provided and larger than the sum ofregions, the trailing space paints as a dashed-border empty headroom slice with the top-of-stack address labelled at the top of the track. When omitted, the track sizes to the sum and no headroom slice appears.- Hex offsets are computed cumulatively. Each region's start = the previous region's end (mebibytes × 1024²). Addresses are formatted as fixed-width hex with a Rust-style underscore mid-split (
0x0000_0000) for legibility.BigIntis used internally so multi-gibibyte ranges don't lose precision at the 53-bit safe-integer ceiling. - Lower addresses at the bottom. The visual convention follows the textbook "what every C programmer should know about memory" diagram:
.textand.rodataat the bottom, the heap growing upward, the stack growing downward from the top. - Cursor: controlled + uncontrolled.
currentRegionaccepts a regionidornull; pass it for fully controlled focus, or usedefaultCurrentRegionfor uncontrolled withonCurrentRegionChangenotifications. Clicking a region swatch or arrow-keying the label list moves the cursor. Up walks toward higher addresses; Down toward lower. SPRINGS.smootheverywhere. Segment sizing animates with the canonical smooth spring.prefers-reduced-motion: reducecollapses every spring to an instant swap.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
regions | readonly MemoryStackVizRegion[] | required | Regions in ascending-address order. The first sits at the bottom. |
totalCapacityMb | number | sum of regions | Total address-range size in MB. Adds a headroom slice when larger than the sum. |
showOffsets | boolean | true | Render the start/end hex address for every region. |
currentRegion | `string | null` | — |
defaultCurrentRegion | `string | null` | null |
onCurrentRegionChange | `(next: string | null) => void` | — |
transition | Transition | SPRINGS.smooth | Spring used for segment-size transitions. |
className | string | — | Merged onto the root via cn(). |
The MemoryStackVizRegion shape: { id: string; label: string; sizeMb: number; tone?: 'accent' | 'warning' | 'success' | 'error' }.
Accessibility
- The figure is
role="figure"with a hidden summary listing every region, its size, and its start/end hex address — screen readers hear the layout wheneverregionschange. - The label list is a
role="list"that owns a single tab stop and accepts ArrowUp / ArrowDown / Home / End to walk the cursor through regions. ArrowUp moves toward higher addresses, matching the visual convention. - Region swatches in the bar and the label list are buttons with
aria-pressedreflectingcurrentRegion, an accessible label naming the region, and a visiblefocus-visiblering on the swatch in the label list. - Color is never the only signal — every region has a textual label, a tone swatch, an MB readout, and (when
showOffsets) a hex start/end address. - Motion respects
prefers-reduced-motion: reduce.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/nn/MemoryStackViz.tsx). The source was a fine-tuning memory-stack widget — four hardcoded buckets (weights, gradients, Adam m, Adam v), GPU-capacity threshold lines (4090 / A100 / 8×A100), discrete sliders for model size and precision, a multiplier annotation, a breathing-pulse hint, and a three-phase narration state machine. The library extract reframes the name around its more general usage: visualising the actual layout of any contiguous address range with hex offsets and arbitrary region tones. The pure plotting primitive: regions in, normalised stack out, with bottom-up address growth, motion-library springs, controlled / uncontrolled cursor focus, and an optional headroom slice whentotalCapacityMbexceeds the sum.