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."

Memory stack. .text (code): 4.00 MB at 0x0000_0000 to 0x0040_0000; .rodata: 2.00 MB at 0x0040_0000 to 0x0060_0000; .data: 6.00 MB at 0x0060_0000 to 0x00C0_0000; .bss: 8.00 MB at 0x00C0_0000 to 0x0140_0000; heap: 96.0 MB at 0x0140_0000 to 0x0740_0000; stack: 8.00 MB at 0x0740_0000 to 0x07C0_0000. Capacity: 256 MB, 132 MB free.
Memory stack256 MB
0x1000_00000x0000_0000
  • .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
Customize
Regions (MB)
4 MB
2 MB
6 MB
8 MB
96 MB
8 MB
Address space
256 MB
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/memory-stack-viz.json

Usage

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

  1. One track, many regions. Each region is a child motion.button whose height animates to its share of the track. The track itself is a single rounded box with flex-col-reverse, so the first region in regions sits at the bottom (lowest address).
  2. totalCapacityMb sizes the address range. When provided and larger than the sum of regions, 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.
  3. 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. BigInt is used internally so multi-gibibyte ranges don't lose precision at the 53-bit safe-integer ceiling.
  4. Lower addresses at the bottom. The visual convention follows the textbook "what every C programmer should know about memory" diagram: .text and .rodata at the bottom, the heap growing upward, the stack growing downward from the top.
  5. Cursor: controlled + uncontrolled. currentRegion accepts a region id or null; pass it for fully controlled focus, or use defaultCurrentRegion for uncontrolled with onCurrentRegionChange notifications. Clicking a region swatch or arrow-keying the label list moves the cursor. Up walks toward higher addresses; Down toward lower.
  6. SPRINGS.smooth everywhere. Segment sizing animates with the canonical smooth spring. prefers-reduced-motion: reduce collapses every spring to an instant swap.

Props

PropTypeDefaultDescription
regionsreadonly MemoryStackVizRegion[]requiredRegions in ascending-address order. The first sits at the bottom.
totalCapacityMbnumbersum of regionsTotal address-range size in MB. Adds a headroom slice when larger than the sum.
showOffsetsbooleantrueRender the start/end hex address for every region.
currentRegion`stringnull`
defaultCurrentRegion`stringnull`null
onCurrentRegionChange`(next: stringnull) => void`
transitionTransitionSPRINGS.smoothSpring used for segment-size transitions.
classNamestringMerged 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 whenever regions change.
  • 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-pressed reflecting currentRegion, an accessible label naming the region, and a visible focus-visible ring 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 when totalCapacityMb exceeds the sum.