Rendering a 300- to 500-page high-resolution document bundle inside an interactive browser workspace presents a classic systems engineering tension: if the frontend fires 300 concurrent HTTP GET requests for 150 DPI page images on mount, browser connection pools saturate, Head-of-Line (HoL) blocking stalls the active viewport page, and decoding hundreds of uncompressed bitmaps simultaneously crashes mobile and desktop browser tabs with Out-Of-Memory (OOM) errors.
To make the Interactive Page Inspector in KortiBundle feel instantaneous even on 400-page court records, we engineered a Multiplexed Persistent WebSocket Binary Streaming & Progressive Layout Hydration Architecture paired with an interactive SVG vector coordinate overlay.
1. Why Multiplexed WebSockets Outperform REST Image Polling for Document Canvases
When a user opens a multi-hundred-page document workspace or drags the scrollbar rapidly from Page 2 to Page 180, traditional HTTP REST image fetching suffers from three bottlenecks:
- Connection Pool Saturation & Stale Queueing: Browsers cap concurrent HTTP/1.1 connections per origin (typically 6) and even over HTTP/2, queueing 200 image render requests causes pages the user scrolled past 3 seconds ago to monopolize server CPU cores while the page currently in the viewport waits at the back of the queue.
- Repeated TLS & Header Overhead: Sending authentication headers, cookies, and CORS preflights across hundreds of individual page-slice requests adds unnecessary latency.
- Synchronous Layout Blocking on Session Creation: Computing optical line geometry and margin artifact masks for all 300 pages before opening the workspace forces the user to stare at a loading spinner for 45+ seconds.
2. Progressive Viewport-First Layout Hydration
Instead of computing full line geometry for every sheet upfront, our backend initializes the session metadata in under 400 milliseconds by reading native page dimensions and marking pages with layout_pending: true. As soon as the React inspector mounts, a progressive hydration loop inspects activePageIdxRef.current on every iteration:
// Viewport-Prioritized Progressive Page Layout Hydration Loop
useEffect(() => {
const pendingSet = new Set<number>();
session.pages.forEach((p, idx) => {
if (p.layout_pending) pendingSet.add(idx);
});
if (pendingSet.size === 0) return;
let cancelled = false;
const runProgressiveHydration = async () => {
while (!cancelled && pendingSet.size > 0) {
const curActive = activePageIdxRef.current;
// Always prioritize the sheet currently in the user's viewport first!
const nextIdx = pendingSet.has(curActive)
? curActive
: pendingSet.has(curActive + 1)
? curActive + 1
: (pendingSet.values().next().value as number);
pendingSet.delete(nextIdx);
const hydrated = await hydrateSessionPageLayout(session.session_id, nextIdx);
if (cancelled) break;
applyHydratedPageToState(nextIdx, hydrated);
}
};
void runProgressiveHydration();
return () => { cancelled = true; };
}, [session.session_id]);
If the user jumps directly to Sheet #140 (or clicks a [Pg 140 · L22] citation in KortiAI), activePageIdxRef.current immediately updates to 139, so the very next hydration cycle (and immediate on-demand fetch) hydrates Sheet #140 without waiting for Sheets #2 through #139!
3. Separating the Underlying Page Raster from the Interactive SVG Vector Layer
Re-rendering a full 150 DPI PDF page on the server every time a user drags a 10, 20, or 30 margin marker by 2 pixels would introduce 300ms of network lag per mouse move. Instead, we decouple the sheet into two layers:
- The Clean Underlying Sheet Image: Streamed once over the persistent WebSocket with old corner page numbers masked out and cached in memory as a Blob URL.
- The Calibrated SVG Vector Overlay: Mounted directly on top of the image with
viewBox="0 0 {page.width} {page.height}"matching the exact PDF point coordinate space (e.g.,595 x 842 ptfor A4). Every 10th-line stamp, connector dash, exclusion crop box, cited-line spotlight band, and pagination badge is rendered as a crisp, hardware-accelerated SVG element that drags at 60 FPS with zero network round-trips.
| Workspace Performance Metric | Synchronous REST + Server Re-Render | Progressive WS + SVG Vector Overlay |
|---|---|---|
| Time to Open 200-Page Bundle in Inspector | 28 to 55 seconds (blocks on all pages) | < 600 milliseconds (instant skeleton + active page) |
| Dragging a 10th-Line Stamp or Page Badge | 250–400ms server round-trip lag per drag | 60 FPS zero-latency SVG vector manipulation |
| Jumping to Page 150 via Citation Click | Waits behind 149 queued HTTP image requests | Immediate viewport prioritization + baseline scroll |
Explore related production architectures, live Kenyan legal tools, and enterprise guides:
- KortiBundle Interactive Page Inspector — Test our real-time WebSocket + SVG vector document workspace live.
- Zero-Allocation File Uploads & Multipart Streaming — Handling large 100MB+ document uploads without worker memory spikes.
- Distributed Tracing Across Asyncio, Celery, and WebSockets — End-to-end observability for real-time streaming backends.