Show3D Storyboard#

Use with Storyboard.

Stories#

S3D-01: Open A Time Series Quickly#

User story: As a user opening a time series or focal stack, I want first paint in about a second for normal working sizes, and still within seconds for heavy stress data, so I can start scrubbing before the workflow feels blocked.

Primary widgets: Show3D.

Data to use: single-panel real 3D stack and a heavy real-derived multi-panel movie such as 12 panels x 32 frames.

Acceptance checks:

  • Load the stack from real backend files or arrays without requiring the laptop to receive every native pixel before first paint.

  • Measure first visible paint, payload size, display bin, and native shape.

  • Verify first frame, current frame label, and histogram render correctly.

  • Confirm display binning is explicit when native pixels are not available.

  • Verify frame labels and per-panel metadata are available before playback, even when higher-resolution detail arrives later.

S3D-02: Match Show2D Visual Language#

User story: As a user comparing datasets side by side, I want multi-panel Show3D to use the same visual language as Show2D: labels, scale bars, colormaps, histogram controls, and compact status text.

Primary widgets: Show3D and Show2D reference gallery.

Data to use: current frame from the same real stack rendered through Show2D for parity.

Acceptance checks:

  • Compare labels, scale bars, color maps, panel borders, stats, and histogram controls against Show2D.

  • Pass cmap=["RdBu", "viridis", ...] for a multi-panel Show3D comparison and verify every panel keeps its own colormap in the Python API, live UI, saved state, static notebook fallback, and standalone HTML export. Hover or select each panel and confirm the Color dropdown reports that panel’s map without changing neighboring panels.

  • S3D-CMAP-1: On a real MoS2 two-panel stack, verify the SSB phase panel renders with RdBu while the DPC phase panel renders with viridis; scrub to another frame and confirm the frame label/canvas update while the panel-specific Color dropdown follows the hovered panel.

  • Compare saved Show3D fallback pixels against a Show2D current-frame gallery for controlled parity tests.

  • Verify one label per panel and no duplicated MP4/GIF labels.

S3D-03: Arrange Movie Panels#

User story: As a user arranging many movie panels, I want a column selector and panel reorder control so I can switch between one row, multiple rows, dense galleries, and the comparison order I want to share.

Primary widgets: Show3D.

Data to use: at least 8 movie panels; include a 12-panel heavy page for release signoff.

Acceptance checks:

  • Change columns through 1, 2, 3, 4, 6, 8, and 12.

  • Verify the menu does not offer impractical counts above 12.

  • Toggle Reorder, drag panels into a new order, reset the order, and confirm panel_order, visible_panels, Show2D handoff, saved state, and HTML export follow the same order.

  • Verify the current frame, zoom center, labels, scale bars, histogram, and contrast do not jump during reflow.

S3D-04: Hide Movie Panels#

User story: As a user screening many movie panels, I want to hide panels and have playback, FFT, stats, export, and saved previews follow the visible set.

Primary widgets: Show3D.

Data to use: multi-panel real or real-derived movie.

Acceptance checks:

  • Hide and restore panels while playback is stopped and while playback is active.

  • Verify frame slider, FFT overlays, stats, exports, and saved previews use only visible panels.

  • Verify hidden panels do not keep stale FFT/cache work alive.

S3D-05: Play And Scrub Smoothly#

User story: As a user playing or scrubbing a movie, I want the image, frame label, histogram, and slider to stay synchronized at the selected FPS.

Primary widgets: Show3D.

Data to use: heavy real-derived multi-panel movie.

Acceptance checks:

  • Press Play/Pause at 30 FPS and verify image and slider stay synchronized.

  • Increase FPS and record whether the slider lags the image.

  • Drag the frame slider slowly and quickly.

  • S3-DN-3: Start with browser-side Gaussian denoise enabled, scrub from the initial frame to a later frame, and verify both canvases remain visibly smooth rather than briefly or permanently showing raw pixels.

  • Use keyboard frame stepping.

  • Change averaging window during playback.

  • Toggle Loop and Bounce and verify end-of-stack behavior.

  • Verify no background flash, stale frame, or delayed label appears.

S3D-05B: Explore Time-Series Dynamics With Playback Presets#

User story: As a scientist inspecting time-series Show3D data, I want playback dynamics that act like temporal lenses, so I can slow down subtle changes, bounce through reversible motion, focus on an event range, or hold key frames without rewriting the notebook.

Primary widgets: Show3D.

Data to use: a real or real-derived time series with a visible event, motion, relaxation, or reconstruction change. Include one exported HTML report because collaborators often review time series outside Jupyter.

Acceptance checks:

  • Keep the main toolbar simple. Put playback style in the playback row next to the frame star, not in the top toolbar More menu.

  • Linear style: play through the current loop range at constant fps; verify frame label, slider, histogram, FFT, and scale bar stay synchronized.

  • Power In, Power Out, and Ease In/Out styles: generate playback_path from the current loop range; verify users still control fps, loop, boomerang, and range from the existing playback row controls.

  • Configure denoise plus a low-pass, high-pass, or band-pass FFT filter before pressing Play; verify transitions stay visibly filtered and smooth, the slider remains honest, and the viewer does not flash raw/noisy frames while cached filtered frames warm.

  • Run at least one organic notebook trial that starts from a plain Show3D(stack) or similarly minimal call. A reviewer should turn Denoise, Filter, FFT, Loop/Bounce, playback style, and speed/range controls on from the widget UI itself, not by preloading every behavior through kwargs.

  • While that organic trial is playing, behave like a real reviewer: scrub the frame slider, pause/resume, drag denoise sigma, drag filter cutoff or band controls, accidentally or intentionally change a nearby display control such as colormap/Smooth, then keep playing. Verify the canvas remains filtered, the frame counter stays honest, and the image does not twitch from competing repaint paths.

  • Use kwargs-heavy repro notebooks only as focused bug reproducers. They do not replace the organic UI-drive trial because they skip the user behavior that often exposes menu, focus, cache, and repaint races.

  • Save state and export HTML; reopen and verify fps, loop, boomerang, range, and playback_path survive, but playback does not unexpectedly start unless explicitly requested.

  • Drive the same presets with FFT visible, denoise/filter enabled, and hidden panels in a multi-panel movie; verify cache counters do not grow while idle and hidden panels do not keep doing work.

S3D-06: Compare Dynamics Across Panels#

User story: As a user comparing dynamics across panels, I want linked zoom and linked contrast to be fast and reversible.

Primary widgets: Show3D.

Data to use: multi-panel stack with shared spatial features.

Acceptance checks:

  • Toggle linked zoom and pan; verify panels move together.

  • Toggle linked contrast; verify contrast changes apply consistently when linked and independently when unlinked.

  • Change scale mode, colormap, Smooth, and histogram range while scrubbing.

  • Resize the grid and verify current frame, labels, scale bars, and histogram UI do not jump unexpectedly.

S3D-07: Inspect FFT In Flexible Layouts#

User story: As a user inspecting reciprocal space, I want FFT layouts on bottom, right, or overlay without changing real-space interaction semantics.

Primary widgets: Show3D.

Data to use: real stack with visible lattice peaks.

Acceptance checks:

  • Toggle FFT bottom layout and verify spacing between Show3D and FFT, panel alignment, resize behavior, and histogram placement.

  • Toggle FFT right layout and verify vertical height aligns with real-space panels and controls remain reachable.

  • Toggle FFT overlay and verify every visible panel receives one overlay.

  • In every layout, verify each FFT tile or inset shows the current shared N.N× multiplier, including uncalibrated data and narrow/mobile layouts; show_zoom_indicator=False must hide the FFT badges.

  • Pause on a known frame, switch browser tabs, and return. Bottom, right, and overlay FFT layouts must restore their image pixels and multiplier without changing the frame or playback state and without another FFT computation.

S3D-08: Control FFT Overlays Independently#

User story: As a user using FFT overlays, I want each overlay centered, cached, independently zoomable, pannable, and draggable with corner snap.

Primary widgets: Show3D.

Data to use: heavy multi-panel movie with FFT overlay enabled.

Acceptance checks:

  • Verify overlay starts centered on FFT center, not an edge or corner.

  • Change overlay size and verify it resizes independently from the real-space panel grid.

  • Drag overlay and release near each corner; verify snap-to-corner works.

  • Wheel over overlay and verify FFT zooms, not the underlying real-space image.

  • Verify the inset multiplier updates with FFT zoom and reset restores 1.0× without changing the real-space multiplier.

  • Use the documented pan gesture over the overlay and verify FFT panning works when zoomed.

  • Verify cached/display-sized FFT does not recompute unnecessarily during playback, frame scrub, scroll, or resize.

S3D-09: Trust FFT Peak Display#

User story: As a user validating FFT peaks, I want suspicious FFT views compared against a reference before trusting the display transform.

Primary widgets: Show3D.

Data to use: real panel/frame where peaks look broad, missing, or too dark.

Acceptance checks:

  • Compare FFT magnitude and peak locations against NumPy or another trusted reference.

  • Distinguish magnitude correctness from display transform/contrast problems.

  • Verify FFT remains readable on black/dark backgrounds.

S3D-10: Export Animations#

User story: As a user making animations, I want GIF and MP4 exports to show only image panels with clean borders, one label per panel, and predictable file sizes.

Primary widgets: Show3D.

Data to use: single-panel, 3-panel, and 2x2 real time-series examples.

Acceptance checks:

  • Export GIF and MP4 panel-only animations.

  • Verify expected frame count, multi-panel layout, live-style labels, scale bar, zoom readout, border/background, playback speed, and file size.

  • Verify quality/speed options are visible and have clear labels.

  • Use labels and title spans containing symbols and inline math such as \lambda=0.03 raw and $\\chi^2$/pixel; verify the panel menu, stats rows, and animation labels render symbols/math and never show raw markup.

  • Run PYTHONPATH=src:. python scripts/widget_show3d_animation_smoke.py when judging whether a GIF is good enough for PowerPoint/email sharing.

S3D-11: Export Shareable HTML#

User story: As a user sharing HTML, I want exact/quantized export labels and sizes to match the Show2D export vocabulary.

Primary widgets: Show3D.

Data to use: single-panel and multi-panel real stacks.

Acceptance checks:

  • Open Export and verify HTML exact/quantized/GIF/MP4 labels and approximate sizes.

  • Collapse controls for presentation-style viewing and verify Export remains reachable from the title chrome in live and standalone HTML.

  • In standalone HTML, verify GIF/MP4 choices remain visible but disabled with a backend-required explanation until a browser-side animation encoder exists.

  • Verify cancellation/status text clears after the documented timeout.

  • Export HTML exact and quantized where supported.

  • S3-EX-2: Open an exact standalone export with display-only denoise enabled. Before any interaction, verify the canvas is denoised; then scrub to another frame and verify the denoise remains applied.

  • Open exported files and drive playback, frame slider, columns, hide panels, FFT overlay, histogram, and reset.

  • In each standalone file, pause with FFT visible, switch away and back, and verify the main image plus bottom/right/overlay FFT canvases restore without pressing Play or moving the frame slider.

S3D-12: Save And Reopen A Notebook#

User story: As a notebook user, I want Cmd+S and reopen to show a compact Show3D fallback that is pixel-matched to a Show2D current-frame gallery.

Primary widgets: Show3D and Show2D reference gallery.

Data to use: Jupyter notebook with real or real-derived stack.

Acceptance checks:

  • Press Cmd+S in JupyterLab and reload/reopen the notebook.

  • Verify saved Show3D static output is visible.

  • Compare fallback against Show2D current-frame gallery in controlled tests.

  • For a single-panel stack, set notebook_preview_frames=[0, mid, last] and notebook_preview_ncols=3, save, and reopen without rerunning the cell. The saved PNG fallback must be a compact contact sheet of exactly those frame indices with the same frame labels, scale bar, colormap, and contrast language as the live current-frame preview.

  • Repeat the save with no notebook_preview_frames and verify the default remains the current frame only.

  • In multi-panel Show3D, set notebook_preview_frames and verify the saved preview intentionally stays one current frame per visible panel rather than multiplying panels by saved-frame count.

  • Draw one circular ROI and then multiple visible ROIs on a single-panel stack, scrub to a representative frame, save, and reopen the notebook without rerunning the cell. The saved PNG fallback must show the current frame with all ROI overlays plus one ROI zoom/crop panel per visible ROI.

  • After drawing circle, rectangle, square, and annular ROIs, call get_roi_geometries() and verify the returned (row, col) center, radius, corners, bounds, visibility, and colors match the visible overlays and survive state_dict() / load_state_dict() while the user scrubs frames.

  • Check metadata.widgets or get_state() for heavy-buffer leaks: frame_bytes, _buffer_bytes, offline stacks, and export payloads must not be present when save_state=False.

S3D-13: Use The Widget On A Phone Or Narrow View#

User story: As a user checking Show3D on a phone or narrow screen, I want playback controls, frame slider, FFT controls, and panel menus to remain reachable.

Primary widgets: Show3D.

Data to use: single-panel and multi-panel stack.

Acceptance checks:

  • Test a narrow mobile viewport.

  • Verify controls wrap, playback controls stay reachable, labels fit, and the frame slider remains usable.

  • Test touch-style drag and scroll gestures.

  • For iPhone-specific claims, serve the page to a physical iPhone Safari test.

S3D-14: Review Many Denoising Or Time-Series Results#

User story: As a user reviewing denoising, drift, focal-series, or time-series outputs, I want many related stacks to open as movie panels quickly, so I can compare temporal behavior without waiting for every native frame to serialize into the browser.

Primary widgets: Show3D.

Data to use: real or real-derived stacks built from 4k x 4k source files. Use at least 12 panels x 32 frames for routine signoff; add 30, 45, or 85 panel/file workflows when the scientific workflow produces that many outputs.

Acceptance checks:

  • Load the stacks from backend file paths or prepared arrays and record backend host, file count, panel count, frame count, native shape, dtype, native bytes, display bin, first-paint time, and initial payload size.

  • Verify a useful binned first frame appears before full-resolution detail or FFT work finishes.

  • Scrub immediately after first paint and verify the image, slider, labels, and histogram stay synchronized.

  • Change columns through 2, 3, 4, 6, 8, and 12 while preserving frame index, zoom anchor, labels, scale bars, and contrast state.

  • Hide and restore panels during playback and verify hidden panels do not keep unnecessary frame or FFT work active.

  • Record whether playback, frame slider, histogram, and overlay interactions remain near the target FPS, or document the limiting case.

S3D-15: Keep Movie Loading And Storage Lightweight#

User story: As a notebook user working with large stacks, I want Show3D to load a compact preview quickly, stream or reveal higher-resolution detail when I ask for it, and save/reopen without embedding huge frame buffers.

Primary widgets: Show3D.

Data to use: single-panel 4k-derived stack and multi-panel real-derived movie such as 12 panels x 32 frames or larger.

Acceptance checks:

  • Compare live Jupyter loading, saved-notebook reopen, standalone HTML export, GIF export, and MP4 export for the same stack.

  • Verify live Jupyter first paint is not blocked on all native frames, FFT overlays, or animation encoders.

  • Press Cmd+S, reload the notebook, and verify the saved fallback is visible and compact.

  • Inspect widget state for heavy-buffer leaks when save_state=False: full frame stacks, FFT caches, detail buffers, and export payloads should not be persisted.

  • Open Export and verify exact float32, quantized uint8, GIF, and MP4 choices use the documented Show3D vocabulary and show approximate file sizes when known.

S3D-16: Stress Playback, FFT, And Sliders On Heavy Movies#

User story: As a scientist inspecting heavy movie data, I want playback, scroll zoom, FFT overlay, histogram, and sliders to stay smooth because a slow movie viewer hides dynamic behavior and wastes analysis time.

Primary widgets: Show3D.

Data to use: 12+ panel real-derived 2k or 4k-source movies, including at least 32 frames. Use larger 30/45/85 file or panel workflows when available and record skipped maximum cases explicitly.

Acceptance checks:

  • Measure first paint, frame scrub, play at selected FPS, high-FPS playback, histogram drag, mousewheel zoom, pan, column reflow, FFT toggle, FFT overlay drag/snap, FFT overlay zoom/pan, and reset.

  • Verify image, frame slider, frame labels, histogram, stats, and playback controls remain synchronized under stress.

  • Verify FFT overlays use cached display-sized FFTs during playback and do not recompute full-resolution transforms unless the user explicitly requests a higher-fidelity FFT path.

  • Verify overlay FFT starts centered, remains readable on dark backgrounds, and can be moved away from scale bars or important image features.

  • Confirm no stale frame, stale FFT, white/yellow flash, blank overlay, or delayed slider state remains after rapid interaction.

  • Repeat the paused tab-away/tab-return path after FFT zoom and pan. Require cached display repaint counters to increase while FFT miss, compute, and metric-compute counters remain unchanged.

  • Add timing and failure notes to the performance log with exact data path, backend host, browser, adapter, shape, frame count, and panel count.

S3D-17: Watch A Live EMD Frame Series In Place#

User story: As a microscopist collecting an in-situ, focal, tilt, or reconstruction series, I want one already displayed Show3D stack to append each completed EMD as a frame, without rerunning the cell, creating another widget, or interrupting inspection and playback.

Primary widgets: Show3D.from_folder(...) in live JupyterLab. Every matching file is one frame in a single stack; it is not a new Show3D widget or an additional gallery panel.

Data to use: Genuine same-shape Velox image EMD files from one real series, added through atomic rename. Use at least three files for routine verification and ten for release signoff. Include an incomplete EMD, an incompatible-shape EMD, and a later compatible EMD. Record source path, shape, dtype, sampling, native bytes, backend host, browser, widget commit, and watch interval. Keep source data and generated reports outside git.

Acceptance checks:

  • Keep folder arrivals on one frame axis regardless of file count. Crossing Show2D’s default 20-panel threshold must leave n_panels == 1, n_pages == 1, and the page controls absent; the frame slider and playback are the navigation. Reject page_size= and page_labels= with a corrective suggestion to use Show2D.from_folder(...) when files should become independent gallery panels. Preserve explicit 5-D/list-of-page Show3D comparisons as a separate constructor workflow.

  • In live JupyterLab, display exactly one Show3D.from_folder(folder, pattern="*.emd", watch=True, watch_interval=1.0, debug=True) object and capture its Python identity, widget model ID, and browser container before later files arrive.

  • Start once with a valid EMD and separately before the first acquisition. The empty-folder case must remain visibly mounted and accept the first stable EMD without a cell rerun; mark it Fail until that behavior exists.

  • Keep one compact accessible watch badge near the folder/title area in stable DOM. Require green-dot Watching only while the actual background worker is alive. Enter Updating while a discovery poll is active and keep it through real candidate validation, append, and authoritative frame/canvas paint. An idle poll may briefly show Updating but must return to Watching without decode, transfer, or repaint. Use amber Waiting for file completion for an incomplete EMD, red Watch error plus corrective detail for a bad shape or worker failure, and gray Stopped or Not watching after stop/close or when liveness cannot be established. A watch=False snapshot has no badge. A restored notebook model, static fallback, or standalone snapshot must never restore a green Watching state without a live worker. Capture browser assertions and screenshots for every state; color alone is insufficient.

  • Complete a genuine EMD after the widget is visible. Verify the frame count, label, slider range, and canvas update automatically without calling poll_folder(). Compare source shape, calibration, representative pixels, and the new frame canvas checksum against read_image(path).array.

  • Preserve the Python object, widget model, browser container, selected source path, bookmarks, starred frame, loop bounds, playback state, FFT, ROI, profile, zoom, pan, contrast, and calibration when natural ordering inserts a new filename before existing frames.

  • Append while Play is active. Verify playback remains active and the image, slider, label, histogram, and frame cache remain synchronized without a blank or stale frame.

  • Defer and retry an incomplete EMD without stopping the watcher or duplicating the frame. Report an incompatible shape without resizing it or blocking a subsequent compatible frame.

  • Measure stable-file-to-Python-append and stable-file-to-first-canvas-paint separately for every arrival; report median and p95 with the poll interval. Backend state alone is not visible-user proof.

  • Verify idle polls do no decode, transfer, repaint, or state-reset work. Exercise idempotent stop/resume, then call close() or free() and prove no watcher thread can mutate the stack afterward.

S3D-19: Export A Shareable Color Movie Without A Dead File#

User story: As a user sharing a color result movie, I want a single HTML that actually opens in a browser, and a clear refusal (not a silently broken 700 MB file) when the movie is too big to embed.

Primary widgets: Show3D.

Data to use: a real RGB stack large enough that the float32 single-file embed exceeds the safe limit, plus a small one that fits.

Acceptance checks:

  • Export the small stack with default options; open the HTML over a local http server and verify true color renders and every frame decodes correctly.

  • Export the large float32 stack; verify export_html refuses with a message naming the estimated size, the safe limit, and the smaller options (encoding='uint8', downsample=, explicit max_mb=).

  • Re-export the large stack with encoding='uint8'; verify it now fits and opens, and the color is visually faithful.

  • Confirm max_mb=<larger> overrides the refusal for a deliberate big export.

S3D-20: Scrub Full-Resolution Movies Over A Remote Jupyter Tunnel#

User story: As a scientist running Jupyter on a GPU workstation but viewing Show3D from my local laptop browser over an SSH tunnel, I want the frame slider to feel real-time even for 3x3 grids of native 2048 x 2048 movie panels, so I can judge dynamics without copying the dataset to the laptop or waiting one to two seconds per slider tick.

Primary widgets: Show3D first; Show2D should follow the same remote transport rule for comparable paged or stacked image review paths.

Data to use: A real or real-derived multi-panel movie on the backend. The canonical stress case is a 3x3 grid of 2048 x 2048 float32 panels, where one native concatenated frame is 150,994,944 bytes. Use the actual lab deployment when possible: laptop browser on phil, Jupyter kernel and data on mjgoat, and an ssh -L tunnel. Synthetic data may be used only as a post-fix control when it preserves the same native spatial shape and per-frame payload.

Acceptance checks:

  • Measure before optimizing. Record Python prepare/wire/encode/trait-set time, browser receive/decode/paint-proxy time, and end-to-end UI latency as now - sendTime, over the real ssh tunnel rather than localhost.

  • Verify the frame-server fast path is actually reachable from the local browser. A kernel-side 127.0.0.1 frame server is not enough when the browser runs on another machine; the browser’s localhost is the laptop, not the GPU workstation.

  • During active slider drag, avoid sending one full native frame through Jupyter Comm for every pointer tick. Prefer local cache, frame-server, or a bounded scrub-preview transport before falling back to native slice_idx commits.

  • Preserve the full-resolution source arrays and committed frame path. Do not bin, crop, overwrite, or silently replace the scientific data to make the interaction faster.

  • If drag-time display uses a reduced preview, print or log one explicit line naming the reduction factor and how to get native resolution. The expected language is equivalent to: displaying {factor}x reduced frames during slider drag; release the slider or zoom/settle the view to request native full resolution.

  • Release the slider and verify native full-resolution pixels remain reachable. The committed frame path must still send or fetch the native frame and zoom/detail inspection must not be limited to the drag preview.

  • Drive the live widget in JupyterLab from the laptop browser: drag slowly, drag quickly, step with keyboard, press Play/Pause, toggle loop/bounce, and verify the image, frame label, histogram, and slider stay synchronized.

  • Record the story result as Pass, Fail, or Not verified with the backend host, browser host, tunnel port, data path, shape, dtype, native bytes, preview bytes/factor if any, debug counters, console errors, and report or notebook URL.