유대선
프로젝트로
·기술 회고·3

Chasing many-pane smoothness: a WebGL renderer pulled for safety, and the error boundary that should have been there all along

Running ~9 streaming Claude panes felt laggy, so I reached for the obvious lever — xterm's WebGL renderer. It compiled and the IPC-batching half landed cleanly, but a separate blank-window crash (opening the summary) exposed a deeper gap: the app had no React error boundary, so any single render throw whited out the entire window with zero diagnostics. I disabled WebGL (the riskier lever, prime suspect for a blank at high pane counts), shipped the safe wins, and added the boundary that turns a future blank into a readable, recoverable error.

The itch: 9 panes, not smooth

Dogfooding with ~9 streaming Claude panes, the terminals felt laggy — nowhere near the buttery feel of a native app. xterm.js ships a DOM renderer by default (fine for one pane, janky for many) and a GPU WebGL renderer addon. The plan was two levers:

  1. WebGL renderer — the big one for terminal paint performance.
  2. Augmentor IPC batching — the app was calling into the Rust backend on every chunk of PTY output to log a parsed event; ×9 verbose TUIs that's a flood. Batch them on a 250 ms throttle into one call.

What landed, and what got pulled

The IPC batching is a clean, low-risk win and stayed. WebGL compiled and ran — but two things made me pull it:

The real lesson was orthogonal to performance

Chasing the blank surfaced the actual gap: there was no React error boundary anywhere. In React, an uncaught throw during render with no boundary unmounts the whole tree → a blank #root, with no error text, no stack, nothing. So any rare render glitch presented as a terrifying total whiteout.

That's the fix that mattered most:

Shipped in this bundle

Honest status

This bundle is mostly features + robustness, not the big perf win I set out for. The headline smoothness lever (GPU rendering) is parked until it can be done safely and verifiably. The next pass is the real one: bring back GPU rendering carefully, cut React re-renders across panes, and treat motion/animation as a first-class polish target — because "feels native" is mostly 60 fps compositing and restraint, not raw throughput.