Render on a worker thread, input on main
The main thread owns the window and event queue only, it polls keys and publishes a snapshot at ~1 kHz and never touches GL; a dedicated render thread makes the context current and owns every GL object. The decoupled rates are the payoff: ~970 Hz achieved input publishing against a 144 Hz frame consumer, with measured end-to-end input latency of p50 ≈ 0.64 ms against a 6.9 ms frame period. The price was paid once, in shutdown ordering: signal stop → render thread deletes its GL objects and detaches → join → destroy the window on main.
would change my mind: a windowing layer without the main-thread event constraint, or a target where second-thread contexts don't exist, the browser build already collapses to single-threaded for exactly that reason.