Skip to content

Production polish: plans Phases 0–5, anatomical rendering and follow-ups - #1

Merged
Andrew-Keenlyside merged 165 commits into
mainfrom
anatomical-rendering
Nov 10, 2026
Merged

Andrew-Keenlyside merged 165 commits into
mainfrom
anatomical-rendering

Conversation

@Andrew-Keenlyside

Copy link
Copy Markdown
Collaborator

The production polish: every item in plans/ (Phases 0–5, 188 items), on top of the anatomical rendering work, plus follow-ups checked live.

What's in it

  • Phase 0–1, checks and blockers: a style/accessibility audit (npm run ui:audit), old-link fixtures, versioned plugin state; Zarr v2 and size-1 axes in the shared readers; plugin crash and data-loss fixes; render service jobs, limits and session lifecycle; plugin load failures and uncaught errors reported, not fatal.
  • Phase 2, one style: tokens for type, spacing, controls and colour; square corners; every panel built from the SDK's shared parts (cards, toggle/checkbox, info, slider, empty state, status line) and updated in place; Neuroglancer's own controls restyled; docs and figures on the new layout.
  • Phase 3, scale: a GPU budget and job queue on the render service; shared byte budgets and cancellable reads in Zarr Vectors and Luxar; Track filtering at 1M streamlines, Segmentation at 100k segments, Annotations at 100k; timeouts on remote requests; streamed exports and long share links as files.
  • Phase 4, accessibility: modal sheets with focus trap and return, arrow keys in menus/tabs/pickers/resizers, combobox palette, reduced motion, keyboard-usable Collections graph and Atlas tree; accessibility measures in the audit.
  • Phase 5, release: CI on pull requests and by hand (typecheck, both suites, runner tests, build, bundle budget); a state-waiting smoke test on local fixtures; jsdom UI tests; a Content Security Policy; Settings › About with versions and a runner version check; a deploy workflow, CHANGELOG.md, pinned dependencies, ESLint, self-hosted fonts, an offline app shell and a loading screen.
  • Follow-ups: the first real Gaussian splat fit (gsplat 1.4.0, so splats are no longer "in progress"); Luxar tested read-only against real CAVE/graphene servers (401 → Sign in); Segmentation checked in Chrome with 96k segments; Google sign-in remembered across tabs again.

Checks run locally

  • npm run typecheck, npm run lint: clean.
  • npm test: 1332 platform + 327 Zarr Vectors tests pass.
  • Runner and render service: 125 passed, 14 skipped (GPU 0).
  • npm run build + npm run budget: within limits.
  • npm run ui:audit: no measure worse than the baseline, 0 page errors, all accessibility measures 0.
  • npm run e2e (smoke test): passes, nothing fetched off the machine.

Known and left open

  • geotiff stays on 3.1.0-beta.0: the stable 3.0.5 reads the same but makes 1.5–7× the requests until chunkmirage passes blockSize (see STATUS.md).
  • The SRB example's view buttons read +x/−x until its zarr.json gets anatomical orientations (prepared, not uploaded).
  • Graphene layers: Neuroglancer's graphene datasource isn't built in, so a CAVE layer added by URL stays untyped; signed-in graphene reads are untested.
  • Segmentation "New segment" when editing in place can reuse an id larger than the coarsest level shows.

…nd, by address or file, the example), a tab per collection, and the collection built in its panel (members, systems, relationships, from the view, download)

The SDK: a scene kind's add (a tab that runs a command, the plugin making its scenes), ctx.ui.sheet, workspace.renameScene.
…es and renders again when it stops; a panel of cards (Remote, Rendering, Lighting, Crops, Output); movie previews, movies as jobs beside a live view; the Volume rendering plugin removed

- No view is sent while the camera moves (drag, wheel, or momentum) unless it goes far from the picture's (20 degrees, 2x, edge): then half size; full size 200 ms after it stops. A frame waits for its camera and distances rather than being drawn flat; triangles kept at the picture's own camera.
- Render refuses, with a message, in a scene with no OME-Zarr or N5 image; offered only for those. Luxar is offered for a segmentation with precomputed meshes.
- Movie and rotation: a small preview in a window, then the movie as a job; movie, splat and still passes go to the GPU one at a time (Renderer.step_beside).
- volume-view keeps only its shared readers.
…virtual Zarr Vectors mesh store, multilod Draco or legacy, the shown segments
…on the left, Tools on the right, bottom collapsed to a status line; Settings sheet (view, sign-in, compute, storage, developer); start card; collapsible cards; shorter text
…c986ab audit), with the decisions, the target style and the checks for every batch
…ted, not read as if current; an older slice that cannot be upgraded says so; old-link fixtures (plain Neuroglancer, platform version 1, a newer release)
…th every plugin on, and the style measures against plans/ui-audit-baseline.json (recorded from this build: it matches the 5c986ab audit)
…nk keys fixed), size-1 t and c axes, fill values in the array's type, a failed chunk named and stopping the read, cm and Å units
…sing it returns there; Escape and a click outside close the top one only; two confirms no longer cancel each other
… reloads once for a stale chunk after a redeploy, and reports uncaught errors in Activity with their stack and a toast, de-duplicated and rate-limited; hidden source maps
…lamped before rendering, out of GPU memory fails the session and frees it, idle sessions closed after 3 min, ffmpeg found at start and its errors kept, a new folder per job, an optional allow-list of remote sources
…reconnect through the service that opened them; per-session state reset; Retry, a frame loop that backs off then stops, and the error shown in the status line; stills as jobs; movie, splat and window inputs kept within the service's limits; the 8-box limit; a note when the host has no ffmpeg
…s the new save left out (erased labels no longer come back); the tool bar and the lasso take any number of segments and points
…syncLru, so their byte count stays exact and the limit holds; a level of any number of cells is planned
…r makes no view, worker or WebGL context and hides no layer; timers and deferred settings check too
…der join the user's undo step (written quietly, once the answer is final); histograms, tag counts and passing ids are counted once per answer and only for open sections, so a progress tick no longer counts every object
…and colour; every control in Plex Sans; buttons and fields 24 px in panels and 28 in sheets; two heading styles; square corners; --faint at 4.5:1; no light-theme rules
…Collections, Luxar, Filters, Anatomical rendering and Track filtering
…ticks before their labels, in groups; chunk statistics in Developer as a table with headings and formatted numbers
…one group with each box before its label, the panels' layout buttons, square grey icons, the contrast editor's bounds and range buttons, the shader editor's colours
…ted for its buttons, fields and tabs, its own control rules removed; one link-button style for button.link and .linkish
…o control (on field and card, in place of title), the one slider with its number, an empty state with its action, a status line for progress and errors, region and set() for updating in place, the app's icons by name, and a stand-in document for panel tests; their shared classes in the stylesheet
…one), in place of a table keyed by title; a Python plugin's panel goes where a tool's does, under Python or the group it names
…l, an empty state with its action, a quiet status line, updated in place; a Tools group; a panel test and a disposal test to copy; Building a panel in the plugin guide; panels, not tabs
…pt with the accounts, so every tab is signed in and a reload keeps it (a token lasts an hour; tokens in the tab remain an environment's choice)
…GeoTIFF with a mask page, served by range, read as levels, placement, tiles and no-data
…locks: 1.5 to 7 times the requests until chunkmirage asks for them)
…he render service; the fit's PATH has the splat Python's own folder (ninja for gsplat's first build), fit.json records the fit's peak GPU memory, views copied so torch may write them; tests of the real fit when gsplat is there; the venv recipe and first build in the runner's README
…un end to end); the docs say where the splat Python's recipe is
…t) is listed with Sign in; tests against MICrONS minnie65_public's recorded 401s
…e CAVE servers' sign-ins are (settings.signIn takes the card to open)
…layer's state once and keep Neuroglancer's hash colours (a segment made active in 0.1 s, was 1 s), hiding a row leaves the long list where it is scrolled (it jumped back to the active segment), the Active field and the tool bar's search box wide enough for long ids
…ly the colours given by hand and the segments with a name or group (15 MB at 96k segments, now 2 KB), and the save's progress drawn at most four times a second (a 512³ OME-Zarr in 14 s, was 41 s)
… of large levels) given a minute; the Settings test's stand-in lists runners for About
…to 32 small chunks at once, fewer as chunks grow (a 256³ box of a sharded store in 1.5–2.2 s, was 7–9 s; a 384³ box in 3.3–5.1 s, was 22–24 s)
…cell: tried four times, then the chunk fails and is read again later, never kept; only a 404 reads as empty (no manifest scan, no zero-filled columns)
…(the same bytes), and a chunk with no ghosts keeps its decoded arrays (a 13 MB cell in 19 ms, was 217; 79 MB in 194 ms, was 1.27 s)
…y to neighbours already at hand and reads no others (they lie off the plane); a 3-D view reads it again whole
…rom two reads, not the whole column (2 of 203 at S-21-46's level 0), and the overview reads only the object values its shader uses
…t, not whatever node_modules links to (a shared node_modules built another checkout's copy)
… as 4 MB ranges at once (520 MB of the 20 µm store in 6.5 s, was 13.4), and stored levels read from a server are kept in the render cache by their stamp (a second session downloads nothing)
…n one image chunk worker, instead of 128–256 MB each (up to ~1.4 GB together): two tools reading one image read and hold it once, and a pass over a whole level keeps nothing
…is computer, collection members), so Neuroglancer skips the .zarray and .zattrs 404s; memory limits sized by the computer's memory; a hint in Activity when the GPU memory is full
…r read it reads nothing again (a 256³ box in 65 ms and 0 MB, was 1.5 s and 12 MB); levels keyed by their metadata, and stores on this computer read afresh by each reader, since tools write them in place
…r level while moving (the fork's byte-limited and movement-aware downloads)
…ds limited by bytes as well as count, a coarser level while moving, adjacent shard reads merged, six decode workers
…object slot and join flag), ids and object values once per object, no index buffer for runs of rows, tangents derived in the shader; the CPU keeps only what picking needs (79 MB cell: 342 MB to 108 MB on the GPU, 29 MB on the CPU)
… the columns its shader reads, which the Segments tab reads too, so each is downloaded once (S-21-46's length: 51 requests and 11.7 MB, was 102 and 23.4)
…lesced reads' kept ranges are charged to the worker's one 768 MB budget instead of their own 256, 64 and 256 MB per store; a whole-cell read is aborted once every chunk waiting for it is cancelled
…d the vertices' spacing on screen, nearest first, within half the GPU memory limit (counted at each chunk's own size once read, else as dense as the coarse chunk over it); bridges only into neighbours at hand; the frontend keeps the packed arrays to upload again (6 mm view of S-21-46: levels 0-3 in 405 MB, was level 3 alone)
…its decode worker loads when that is a worktree of the fork (the suite hung on every zstd read)
@Andrew-Keenlyside
Andrew-Keenlyside merged commit 1ed9a13 into main Nov 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant