Skip to content

Prefetch low-res CT on card hover (viewer feels instant)#79

Merged
aperson30 merged 1 commit into
mainfrom
perf/viewer-load-speed
Jul 18, 2026
Merged

Prefetch low-res CT on card hover (viewer feels instant)#79
aperson30 merged 1 commit into
mainfrom
perf/viewer-load-speed

Conversation

@aperson30

Copy link
Copy Markdown
Collaborator

What

Extends the existing hover warm-up on dataset cards. Today onMouseEnter warms the viewer JS chunk (prefetchViewer); this also warms the low-res CT after a short hover dwell, so clicking a card pays only decode + render instead of the download. This is the biggest perceived-latency win from the speed plan.

Volume responses are immutable (Cache-Control: max-age=604800, immutable), so a prefetched CT is reused straight from the browser cache on click.

Safety (shared JHU infra)

  • CT only — never the mask (halves bandwidth)
  • Dedupe — each case fetched at most once per session
  • Global concurrency cap (2) — hovering across the grid can't stampede the backend. This cap is also why it's safe before the low-res batch finishes: worst case is a couple of full-res CTs in flight, not the whole grid.
  • 150ms hover dwell — skimming across cards doesn't fire fetches; cleared on mouse-leave.

Files

  • helpers/prefetchVolume.ts (new) — queued, capped, deduped low-res CT prefetcher
  • components/Preview.tsx — dwell-timer on the card hover, cleared on leave

Testing

  • tsc -b clean
  • 101/101 frontend tests pass

Not included (deliberately) — #3 and #4 from the speed plan

Both rewrite the viewer's WebGL init/load path, which can't be validated locally (jsdom has no GPU; this checkout has no image data), so shipping them blind risks black-screening the viewer:

Recommend doing those as a follow-up with testing against real data on the server.

Cards already warm the viewer JS chunk on hover (prefetchViewer). This also
warms the low-res CT after a short hover dwell, so clicking a card pays only
decode + render instead of the download. Volume responses are immutable
(7-day cache), so the prefetched CT is reused straight from cache on click.

Bandwidth-safe on shared infra: CT only (never the mask), each case fetched
at most once, a global concurrency cap so hovering across the grid can't
stampede the backend, and a 150ms hover dwell so skimming doesn't fetch.
@aperson30
aperson30 merged commit 86065fa into main Jul 18, 2026
7 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