Man I wish Xonotic made this official and added a WebSocket prototol to official multiplayer server program so it could connect to public servers too. It's awesome you got P2P for it.
Hey HN! I've been using Claude Code a lot lately and got curious whether it could port a full open-source game to run in the browser. Xonotic (a fast open-source arena FPS, think Quake III / Unreal Tournament) seemed like a good candidate: it's built on the DarkPlaces engine (real C + OpenGL), ships gigabytes of assets, and has actual multiplayer.
It's fully playable in the browser, no install or plugins. Pick a map like g-23 to drop into a match against bots.
Some of the technical work I wanted to highlight that I/Claude focused on that went into making it actually fast:
- The engine runs off the main thread. DarkPlaces is compiled to WASM/WebGL2, but the whole engine runs on a worker via Emscripten's PROXY_TO_PTHREAD, with the WebGL context owned directly by the worker through an OffscreenCanvas (zero cross-thread GL dispatch). That let me remove Asyncify entirely — the payoff is a steady frame loop (~4 ms/frame, 99.8% of frames under 16 ms in a profiled match).
- GPU texture transcoding. Every texture is Basis Universal / KTX2, transcoded at load time to whatever compressed format your GPU supports (BC7 on desktop, etc). That took the texture set from ~5.3 GB of TGAs down to a few hundred MB on disk and cut GPU memory ~4×. Audio is re-encoded to Ogg Vorbis.
- Streamed on-demand filesystem. Nothing is bundled. The engine reads through a virtual filesystem backed by Cloudflare R2; only a tiny boot set loads upfront, then each map prefetches its working set in parallel and streams the rest as surfaces actually draw. A full map's assets dropped from ~2.3 GB to ~320 MB per session.
- SIMD. Built with -msimd128, so the math and skeletal-animation paths vectorize to wasm128.
- Hosting. Cloudflare R2 (zero egress) behind a Worker, with COOP/COEP headers for SharedArrayBuffer/threads. Assets are immutable-cached and the engine binary revalidates, so reloads are cheap.
Multiplayer is peer-to-peer. Click Host Game and you get a 6-character invite code; a friend enters it and you connect directly browser-to-browser over a WebRTC DataChannel (configured unreliable/unordered to match the engine's UDP netcode).
A tiny Cloudflare Worker only relays the one-time WebRTC handshake — once you're connected, no game traffic touches any server. I tested a real 1v1 between Edmonton and Bangkok and it held up across the Pacific.
Would love feedback, so please report any bugs or glitches here and I'll patch asap.
Man I wish Xonotic made this official and added a WebSocket prototol to official multiplayer server program so it could connect to public servers too. It's awesome you got P2P for it.
Thank you! I already added Websocket support actually, along with dedicated client/server, just have to push it as this point.
Also if you like it please upvote it, share it, etc!
Hey HN! I've been using Claude Code a lot lately and got curious whether it could port a full open-source game to run in the browser. Xonotic (a fast open-source arena FPS, think Quake III / Unreal Tournament) seemed like a good candidate: it's built on the DarkPlaces engine (real C + OpenGL), ships gigabytes of assets, and has actual multiplayer.
It's fully playable in the browser, no install or plugins. Pick a map like g-23 to drop into a match against bots.
Some of the technical work I wanted to highlight that I/Claude focused on that went into making it actually fast:
- The engine runs off the main thread. DarkPlaces is compiled to WASM/WebGL2, but the whole engine runs on a worker via Emscripten's PROXY_TO_PTHREAD, with the WebGL context owned directly by the worker through an OffscreenCanvas (zero cross-thread GL dispatch). That let me remove Asyncify entirely — the payoff is a steady frame loop (~4 ms/frame, 99.8% of frames under 16 ms in a profiled match).
- GPU texture transcoding. Every texture is Basis Universal / KTX2, transcoded at load time to whatever compressed format your GPU supports (BC7 on desktop, etc). That took the texture set from ~5.3 GB of TGAs down to a few hundred MB on disk and cut GPU memory ~4×. Audio is re-encoded to Ogg Vorbis.
- Streamed on-demand filesystem. Nothing is bundled. The engine reads through a virtual filesystem backed by Cloudflare R2; only a tiny boot set loads upfront, then each map prefetches its working set in parallel and streams the rest as surfaces actually draw. A full map's assets dropped from ~2.3 GB to ~320 MB per session.
- SIMD. Built with -msimd128, so the math and skeletal-animation paths vectorize to wasm128.
- Hosting. Cloudflare R2 (zero egress) behind a Worker, with COOP/COEP headers for SharedArrayBuffer/threads. Assets are immutable-cached and the engine binary revalidates, so reloads are cheap.
Multiplayer is peer-to-peer. Click Host Game and you get a 6-character invite code; a friend enters it and you connect directly browser-to-browser over a WebRTC DataChannel (configured unreliable/unordered to match the engine's UDP netcode).
A tiny Cloudflare Worker only relays the one-time WebRTC handshake — once you're connected, no game traffic touches any server. I tested a real 1v1 between Edmonton and Bangkok and it held up across the Pacific.
Would love feedback, so please report any bugs or glitches here and I'll patch asap.