Show4DSTEM Storyboard#
Stories#
S4D-01: Open 4D-STEM Data Quickly#
User story: As a 4D-STEM user opening a scan, I want a useful virtual image and diffraction preview in about a second for normal preview sizes so I can start inspecting data immediately.
Primary widgets: Show4DSTEM.
Data to use: real or tutorial 4D-STEM stack; include full and binned preview variants when available.
Acceptance checks:
Load from Jupyter and exported HTML when supported.
Measure first visible paint, scan shape, diffraction shape, dtype, native bytes, binning/downsampling, and WebGPU availability.
Verify virtual image and diffraction panels render with labels, scale bars, and correct units.
Verify frontend-only pan/zoom/detector interactions do not make the kernel busy unless backend recomputation is expected.
S4D-02: Relate Scan Position To Diffraction Pattern#
User story: As a scientist inspecting 4D-STEM, I want scan-position movement in the virtual image to update the diffraction panel immediately.
Primary widgets: Show4DSTEM.
Data to use: real 4D-STEM scan with recognizable diffraction variation.
Acceptance checks:
Click and drag scan position across the virtual image.
Verify diffraction updates at the current scan position and labels/readouts stay synchronized.
Hover scan positions without clicking and verify hover coordinates/readouts update for the hovered position without committing a new selected scan position or changing the diffraction panel until the user clicks or drags.
Use keyboard or slider navigation if available.
Record FPS or latency for scan-position movement.
S4D-03: Tune Virtual Detectors#
User story: As a 4D-STEM user, I want BF, ABF, ADF, HAADF, and custom detector controls to update the virtual image interactively.
Primary widgets: Show4DSTEM.
Data to use: real 4D-STEM data with a visible central disk.
Acceptance checks:
Switch detector presets and verify virtual image changes.
Drag detector masks/rings and verify the virtual image updates without visible lag.
Verify detector radius/angle labels are scientifically meaningful.
Compare at least one detector result against a Python/reference computation for correctness-sensitive changes.
S4D-04: Inspect Diffraction Details#
User story: As a diffraction user, I want to pan, zoom, change contrast, and inspect diffraction features without losing the linked scan context.
Primary widgets: Show4DSTEM.
Data to use: real diffraction stack with visible Bragg/disk features.
Acceptance checks:
Pan and zoom the diffraction panel.
Change diffraction contrast, colormap, log/linear scale, and smoothing.
Hover diffraction features without selecting a new scan position or detector and verify diffraction readouts follow the hovered pixel while detector and virtual-image controls remain scoped to the explicitly selected state.
Verify detector overlays remain aligned during pan/zoom/resize.
Verify colorbar/histogram controls are readable on dark and light displays.
S4D-05: Use WebGPU And Fallback Paths Correctly#
User story: As a user on different hardware, I want the right compute path for the surface I am using: an encoded acquisition on MPS or CUDA for live Python-backed work, WebGPU for browser/offline interaction, and a clear error when acceleration is not available.
Primary widgets: Show4DSTEM.
Data to use: WebGPU-capable Mac/browser, MacBook MPS load path, CUDA/Torch workstation path when available, plus a fallback browser or disabled WebGPU environment when possible.
Acceptance checks:
Record WebGPU adapter availability in the report.
Record the GPU backend and data path: encoded acquisition on CUDA or MPS, an array or tensor, or browser WebGPU.
Verify accelerated detector/virtual-image updates when WebGPU is available.
Verify MacBook live-Jupyter browsing loads the acquisition encoded on MPS and computes virtual images on it for first-pass review.
Verify
backend="webgpu"exported/offline pages use browser WebGPU and do not need Python, Torch, or MPS after export.Verify WebGPU unavailability produces a clear corrective error.
Do not claim encoded-acquisition performance from an array or tensor input.
S4D-06: Save, Export, And Reopen 4D-STEM Views#
User story: As a notebook or sharing user, I want a compact two-panel saved preview and shareable export that preserve the scientific context and make the export precision obvious.
Primary widgets: Show4DSTEM.
Data to use: real or tutorial 4D-STEM notebook.
Acceptance checks:
Press
Cmd+Sand reload/reopen the notebook.Verify the static two-panel fallback is visible.
Open Export and verify the menu follows the same vocabulary as the other storyboards:
HTML uint8for compact browse export,HTML fullorHTML uint16for count-preserving export, with approximate size when known.Export compact uint8 HTML and reopen it.
Export full/uint16 HTML or folder HTML when supported and reopen it.
Drive scan position, diffraction pan/zoom, detector controls, and contrast in the exported page.
Check lightweight save state for heavy-buffer leaks.
Verify export status clears after completion/cancel in the same way as Show2D and Show3D.
S4D-07: Use 4D-STEM On A Phone Or Narrow View#
User story: As a user checking a 4D-STEM result on a phone or narrow screen, I want virtual image, diffraction, and detector controls to remain reachable.
Primary widgets: Show4DSTEM.
Data to use: compact real or tutorial 4D-STEM export.
Acceptance checks:
Test a narrow mobile viewport.
Verify panels stack or resize intentionally and labels remain readable.
Test touch-style scan-position movement, diffraction pan/zoom, detector control, and menu access.
For iPhone-specific claims, serve the page to physical iPhone Safari.
S4D-08: Export U8 And Full Data With Honest Reducers#
User story: As a 4D-STEM user sharing data, I want compact U8 HTML for quick browser inspection and full/count-preserving export when quantitative detector counts matter, so collaborators know when a view is browse-quality and when it can be used for quantitative checks.
Primary widgets: Show4DSTEM.
Data to use: real or real-derived 4D-STEM data with detector counts above 255 and a smaller count-limited control dataset where U8 should be nearly lossless.
Acceptance checks:
Export
encoding="uint8"withdownsample=1and with detector downsample values such as 2, 4, and 8; verify the UI and status text identify it as compact/browse U8 data.Verify U8 detector downsample uses the documented reducer, currently mean/average, so detector blocks do not immediately clip and wash out the bright-field disk.
Export
encoding="full"oruint16where supported and verify detector counts are preserved.When full/uint16 export is downsampled, verify the reducer is scientifically explicit. Prefer sum for count-preserving detector binning when the exported dtype can hold the result; use mean only when the goal is browse/display stability and label it that way.
Compare at least one compact U8 export and one full/uint16 export against a Python reference for detector pixel values, virtual BF/ADF sums, and a custom mask.
Confirm exported HTML opens without a Python kernel and that scan-position movement, detector masks, diffraction contrast, and virtual images remain interactive.
S4D-10: Stress Export And WebGPU Reopen#
User story: As a user sharing large 4D-STEM screening results, I want export to finish in a practical time and the reopened artifact to stay responsive, so large browser-shareable views do not become dead files.
Primary widgets: Show4DSTEM.
Data to use: the largest real or real-derived 4D-STEM dataset available on the HPC/workstation backend for routine testing, plus a smaller deterministic dataset for reference parity.
Acceptance checks:
Measure export time, exported file/folder size, first paint after reopen, and WebGPU adapter availability.
Reopen U8 HTML and full/folder HTML where supported; verify no Python kernel is required for the expected interactions.
Drag scan position, detector ring, detector mask, diffraction pan/zoom, and contrast controls; record FPS or latency.
Verify folder export clearly fails or explains what is missing if the companion data folder is moved.
Add timings, reducer choice, dtype, downsample, browser, and backend host to the signoff report.
S4D-11: Use MacBook MPS For Live Loading And U8 Export#
User story: As a MacBook user opening large 4D-STEM data, I want first-pass browsing on the Apple GPU at native detector sampling without exhausting unified memory, and to use detector-binned U8 only when I explicitly choose a compact export.
Primary widgets: Show4DSTEM.
Data to use: a real 4D-STEM master file on a MacBook or a MacBook-connected Jupyter server, plus a smaller deterministic fixture for export parity.
Acceptance checks:
Load with
load(path, backend="mps")and constructShow4DSTEMfrom that acquisition. The acquisition stays encoded at full detector resolution; there is no load-time detector bin or dtype cast.Record load time, first paint, dtype, encoded bytes (
resident_bytes), dense bytes (logical_bytes), and unified-memory pressure.Export full-detector WebGPU/HDF5 HTML when the gate is native detector behavior; export compact HTML with
encoding="uint8"only as an explicitly labeled preview.Verify reopened HTML uses browser/WebGPU for interaction when available, not the Python MPS backend.
Compare one virtual detector and one diffraction frame against a Python reference at the same binned/U8 precision, and separately document any expected clipping from U8 browse data.
Repeat with
encoding="full"oruint16when the data size allows, and verify count-preserving expectations separately from the U8 browse path.
S4D-12: Explain Raw Metal MPS Versus Torch-MPS#
Retired. The MPS-specific raw-Metal viewer was removed; MPS acquisitions from
quantem.gpu.io.load open in the same bounded Show4DSTEM view as CUDA. Use
S4D-11 for MacBook signoff.
S4D-13: Keep GPU Memory Lifecycle Outside The Viewer UI#
User story: As a user running many heavy 4D-STEM notebooks, I want GPU memory to be released by backend/session lifecycle controls rather than by a scientific viewer button, so the viewer stays focused on inspecting data and does not hide ownership of GPU resources.
Primary widgets: Show4DSTEM, plus backend loader/session tooling.
Data to use: repeated open/close of a large MPS or CUDA-backed 4D-STEM dataset.
Acceptance checks:
Verify closing/deleting a widget view does not imply the backend data object or GPU allocation is freed unless the owning Python object/session is also released. The exception is
Show4DSTEM.from_folder(...), which owns the acquisitions it loaded: verifyfree()andclose()close them.Verify the documented cleanup path is backend/session level: delete or replace the loaded data object, clear references, stop/restart the kernel, or use the backend-specific cache cleanup utility when one exists.
Verify notebook save/reopen does not persist GPU buffers or export payloads when
save_state=False.Verify exported HTML has no live Python GPU allocation and therefore does not need a “free GPU” control.
If a future GUI exposes memory status, verify it reports backend ownership and links to cleanup instructions instead of pretending the viewer alone can free all GPU memory.
S4D-14: Watch A Live 4D-STEM Acquisition Folder In Place#
User story: As a microscopist collecting 4D-STEM, I want one already mounted Jupyter Show4DSTEM to discover every newly completed acquisition, expose it exactly once without rebuilding or silently changing precision, and remain interactive while incomplete detector files finish writing.
Primary widgets: Show4DSTEM.from_folder(...). Test backend="cuda"
and backend="mps" separately; both load encoded acquisitions into the same
bounded comparison view, but their decoders and memory differ. CPU is only a
deterministic unit-test reference. Standalone HTML is a snapshot and does not
continue watching a filesystem.
Data to use: A temporary watched folder and at least three genuine 4D-STEM
acquisition groups. Begin with one ready *_master.h5. Introduce a second
master before one linked detector-data file exists, then complete it; add a
third compatible master atomically. Record master/chunk paths, scan and detector
shape, source and loaded dtype, encoded and dense bytes, backend host,
selected GPU, and widget commit. Tiny generated HDF5 is a CI lifecycle
control only and does not establish real-workflow signoff.
Acceptance checks:
Mount
Show4DSTEM.from_folder(folder, watch=True, watch_interval=..., ...)before the later masters arrive. Capture the Python identity, widget model ID, browser container, Dataset/page, panel order, stars, hidden panels, detector ROI, scan cursor, zoom, and playback state.Keep one compact accessible watch badge near the folder/title area in stable DOM for both CUDA and MPS. Require green-dot
Watchingonly while the actual watcher worker is alive. EnterUpdatingwhile discovery is active and keep it through real master/chunk validation and append. An idle poll may briefly showUpdatingbut must return toWatchingwithout decode, transfer, or repaint. If an arrival has a tile on the visible page, remainUpdatinguntil the new tile’s virtual image is painted. Use amberWaiting for file completionwhile a master/chunk is incomplete or not yet stable, redWatch errorwith corrective detail for a bad contract, paint failure, or worker failure, and grayStoppedorNot watchingafter stop/close or when liveness cannot be established.watch=Falsehas no badge. A restored notebook model, static fallback, or standalone snapshot must never restore greenWatchingwithout a live worker. Capture assertions and screenshots for every state; never infer state from color alone or leave a false green badge after exit.A missing, corrupt, unopenable, or still-changing linked data file must not append or load. Once the master and every required link are readable and stable, append it exactly once in deterministic acquisition order. A known master rewrite or removal must not duplicate or silently delete the active scientific view.
Measure two visible stages separately: filesystem-ready to Dataset label, count, page control, or reserved placeholder paint; then selecting/requesting the new dataset to first virtual-image and diffraction paint. Do not call a Python trait update alone “append-to-paint.”
Load each arrival into encoded GPU storage at full detector resolution and append it to the comparison. Do not reload existing acquisitions or silently change shape, dtype, detector bin, or scan bin. Record encoded bytes per master and total GPU memory after each append. Hidden panels are excluded from compare recompute.
Repeat through
Show4DSTEM.from_folder(..., backend="mps")on Apple Silicon and verify append ordering and unified-memory use.Run a small synthetic CPU reference only in tests for lifecycle correctness; production must not route through it. On the same genuine source, compare a virtual image and a diffraction pattern against a reference computed from bounded
read(scan_region=...)tensors.After append, verify
compare_dp_mode="selected"follows the clicked new dataset. If the average-DP mode is part of the change, also verifycompare_dp_mode="average"matches a CPU reference over the current visible ready page, excluding hidden and incomplete datasets. Compare virtual images and diffraction patterns at two or more scan positions.Drive the live path in real JupyterLab through the in-app browser while files arrive. Capture before/after screenshots, console errors, Debug UI FPS and folder/page/cache/memory counters, detector drag, scan movement, diffraction pan/zoom, page flip/playback, and both latency stages.
Verify
stop_folder_watch()is idempotent and restartable.close()orfree()must join the watcher and the background folder fill and close the acquisitionsfrom_folderloaded; a file arriving after cleanup must not mutate the widget.
S4D-15: Sign Off Real Heavy 4D-STEM Performance#
User story: As a microscopist deciding whether Show4DSTEM is ready for a real acquisition session, I want one report that proves the viewer loads a large master quickly, chunks memory safely, appends new masters, and stays interactive in the browser.
Primary widgets: Show4DSTEM with an NVIDIA/CUDA backend when available,
plus standalone exported HTML. Use --backend mps for MacBook checks.
Data to use: local real *_master.h5 files from a lab workstation or
HPC-backed acquisition folder. Do not commit these files or their generated
HTML reports to GitHub.
Acceptance checks:
Run
PYTHONPATH=src:. python scripts/widget_show4dstem_heavy_signoff.py --backend cudawith an explicit local real-data root orQUANTEM_WIDGET_4DSTEM_ROOTS.Verify first-master load time, widget build time, backend shape, dtype, device, resident memory, and GPU memory before/after are in
show4dstem-heavy-signoff-report.json.Verify at least one additional ready master is measured through the folder append path, with its load and append-to-paint timing.
After multiple masters are loaded, drive the Dataset/frame slider end to end and record flip latency/FPS. A report that only measures first load does not prove the real browsing workflow.
Verify standalone HTML export records explicit
uint8/uint16and detector-bin settings, output size, and export time.Drive the exported HTML in Chromium and verify WebGPU/browser information, Dataset/frame flip FPS, virtual-detector drag FPS, scan-position drag FPS, recompute latency, and wheel-zoom FPS are recorded.
Treat
--skip-browseras backend/export debugging only, not performance signoff.For capacity, run a separate probe with 30-40 ready masters at full detector resolution. Passing means either every encoded acquisition loads and browser flip-around is measured, or the report fails clearly with the maximum loaded master count, allocation error, and GPU cleanup evidence. Do not call a 30-40 file workflow supported just because a smaller set is smooth.
S4D-16: Screen Many 4D-STEM Datasets In Multiple Mode#
User story: As a microscopist reviewing a session with many related 4D-STEM acquisitions, I want Show4DSTEM to show many virtual images at once while sharing the diffraction ROI and scan cursor, so I can quickly decide which datasets are useful, hide bad ones, star good ones, and preserve that curation for later notebook cells or shared HTML.
Primary widgets: Show4DSTEM in view_mode="multiple" with 5D data or a
list of encoded acquisitions.
Data to use: 8-14 binned real or real-derived 4D-STEM datasets for routine browser smoke; 30-40 ready masters on CUDA or MPS for heavy signoff when the backend and memory budget allow it.
Acceptance checks:
Construct
Show4DSTEM(..., view_mode="multiple", compare_cols=...)from multiple datasets and verify the multiple grid renders all ready panels without stacking encoded acquisitions into one dense array.Verify desktop
compare_colsis a maximum column count and the phone or narrow viewport caps the grid at two columns with readable tiles.Verify
compare_panel_gap_px=0removes horizontal and vertical gutters between multiple panels, and nonzero values intentionally restore spacing.Scroll over multiple tiles and the single-panel diffraction/virtual-image canvases; verify wheel input zooms the image instead of scrolling the page, and zoom-out behaves symmetrically.
Toggle
compare_dp_modebetween"average"and"selected"; verify the diffraction panel either averages visible multiple panels or follows the clicked dataset.Star at least one useful dataset and hide at least one rejected dataset from the GUI; verify the visible panel count, labels, and selected dataset remain coherent.
Reorder panels by drag or click-then-click reorder mode; verify dynamic order changes before/after release and the saved order is still visible after reset/show-all actions.
Round-trip
compare_panel_order,compare_hidden_panels,compare_starred_panels, andcompare_dp_modethroughstate_dict()/load_state_dict()and through exported HTML.Drive the same workflow in a physical phone browser when making iPhone/Safari claims; Chromium mobile emulation is only a pre-check.
Record dataset count, ready count, backend, dtype, detector bin, grid column count on desktop/mobile, FPS, and whether the artifact is live Jupyter or standalone HTML.
S4D-17: Page A Folder Safely On One CUDA GPU#
Retired. Show4DSTEM.from_folder(...) loads every compatible master into
encoded GPU storage at full detector resolution, so there is no raw-residency
paging, page_budget, or eviction to sign off. Folder watching is covered by
S4D-14.
S4D-18: Pool Multiple GPUs And Stream Pages Progressively#
Retired. Folder viewers load onto one CUDA device or the Apple GPU
(backend=, device=); the gpus= placement and progressive page
loading were removed. Folder watching is covered by S4D-14.
S4D-19: Reopen A Folder With Persistent Scientific Previews#
Retired. The persistent folder preview cache was removed. A reopened folder loads its masters into encoded GPU storage again; S4D-14 covers the folder lifecycle.
S4D-20: Prove Folder Endurance Overnight#
User story: As a scientist leaving a large acquisition folder open overnight, I want the folder viewer to stay truthful and responsive on one NVIDIA GPU, so an ended runner or a green badge cannot hide a stalled, memory-leaking, or stale scientific view.
Primary widgets: Show4DSTEM.from_folder(...), which loads every ready
master into encoded GPU storage at full detector resolution, in fresh Python
processes, plus its browser frontend in a real JupyterLab session. Watch-state
correctness stays canonical in S4D-14 rather than being repeated here.
Data to use: One compatible real acquisition series with linked detector chunks when present (the runner defaults to at least 82 ready masters). Use a staged watched-folder view of the real files for arrival tests so the source acquisition is never rewritten. Synthetic data is a CI control only.
Acceptance checks:
Run
scripts/widget_show4dstem_folder_overnight.py --source ... --device N. It waits for the selected physical GPU to become idle, then runs every case in a fresh child process withCUDA_VISIBLE_DEVICESfixed before Torch imports.Repeat fresh-process opens (default five) and record the time to the first viewer and to the complete folder (
wait_for_folder()).Run the endurance case until both its cycle count (default 100) and its clock budget (default four hours) are met. A canonical cycle requests pages, performs rapid page navigation, stars and hides panels and verifies the state persists, and switches selected/average diffraction. A partial or restarted cycle does not count as completed.
Fail when allocator memory grows by more than the configured limit (default 256 MiB) across the endurance case, on CUDA illegal-address or out-of-memory errors, or on a stuck fill or watch worker. Report memory high-water marks even when the gate fails.
Write an atomic live report and heartbeat throughout the run, record host, widget commit and dirty-diff identity, GPU and filesystem snapshots, and never mutate source data or terminate processes the run did not create.
The runner owns the backend gate (
S4D-20-backend). The browser gates (S4D-14-live-arrival-browserandS4D-20-browser) stay pending until a live JupyterLab drive attaches screenshots, console output, watch-badge states, and paint/FPS evidence; a Python trait publication or backend worker completion is not paint proof.On normal completion, interruption, and failure, call the public cleanup path and prove the watcher, folder fill, notebook, browser, and tunnel processes owned by the run are gone. Record final GPU memory against baseline.