Three browser changes between 2018-2023 put HL2 in your tab

4 min read 1 source explainer
├── "The real unlock isn't WebAssembly performance — it's a chain of four browser primitives finally landing together"
│  └── top10.dev editorial (top10.dev) → read below

WASM has been near-native since 2021, so framing HL2-in-a-tab as a WASM milestone misses the point. The actual unlock is the convergence of SharedArrayBuffer (re-enabled behind cross-origin isolation), pthreads, WebGL2, and OPFS — none individually novel, but only deployable together recently.

├── "Spectre mitigations and cross-origin isolation requirements quietly killed WASM gaming for years"
│  └── top10.dev editorial (top10.dev) → read below

SharedArrayBuffer was disabled across browsers in January 2018 due to Spectre, killing multithreaded WASM for three years. Even after its 2021 return behind COOP/COEP headers, the deployment burden — requiring every subresource to opt in via CORP and preventing third-party iframe embedding — quietly sank most attempted WASM gaming demos between 2018 and 2022.

└── "Running HL2 in a browser tab is a meaningful WebAssembly milestone"
  └── @panza (Hacker News, 613 pts) → view

The HN submission framing and the 613-point community reception treat the project as a watershed moment for WASM, demonstrating that a full 2004 AAA game engine can stream ~2GB of assets and run at 75-85% of native performance entirely in a browser tab.

What happened

A project at hl2.slqnt.dev boots the full 2004 Source engine build of Half-Life 2 inside a browser tab, streaming ~2GB of assets and running the campaign at 75-85% of native performance. The HN thread (613 points) treated it as a milestone for WebAssembly. That framing is half-right and half-misleading.

WebAssembly performance has been within a rounding error of native since roughly 2021. The reason HL2-in-a-tab didn't ship in 2021 is that three other browser primitives weren't ready, and the deployment story for the one that was ready (SharedArrayBuffer) had been deliberately broken by Spectre mitigations in 2018. The actual unlock is a chain of four browser changes that landed at different times across different vendors, and the project is interesting mostly as proof that the chain is now complete.

The stack underneath slqnt.dev is Emscripten compiling the Source engine to WASM, pthreads via SharedArrayBuffer, WebGL2 for rendering, and OPFS (Origin Private File System) for asset caching. None of those are novel individually. The interesting question is what each one had to become for this to be deployable by one person on a weekend.

Why it matters

SharedArrayBuffer is the load-bearing piece. In January 2018, every major browser disabled it in response to Spectre. Multithreaded WASM — which the Source engine needs for audio, networking, and physics on separate threads — was dead for three years. It came back in 2021 behind cross-origin isolation: the page has to send `Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp` headers, and every subresource has to opt in via CORP. This is the boring deployment requirement that quietly killed half the WASM gaming demos people tried to ship between 2018 and 2022 — you couldn't embed a multithreaded WASM game in an iframe on someone else's site without their cooperation, and you couldn't load third-party assets without CORP headers.

OPFS landed across all three engines in 2023. Before OPFS, caching 2GB of game assets meant IndexedDB — a key-value store dressed up to look like a filesystem, with serialization overhead on every read and aggressive eviction under storage pressure. OPFS gives you actual file handles with synchronous reads from a worker thread, isolated to the origin. Loading a `.bsp` map file is now a `FileSystemSyncAccessHandle.read()` call, not a `IDBObjectStore.get()` followed by an ArrayBuffer copy. The slqnt.dev project caches the entire game on first load and treats the browser like the local disk it now actually is.

WebGL2 got the extensions Source actually needs. Stock WebGL2 from 2017 was missing several things the Source renderer assumes: `EXT_color_buffer_float` for HDR-ish lighting, `EXT_texture_filter_anisotropic` for the high-res texture pack, `OES_draw_buffers_indexed` for the deferred-ish pipeline. By 2023 all three were broadly shipped. The remaining gap — true compute shaders — gets papered over by running fragment shader tricks, which is fine for a 2004 engine but is the wall a 2024-era engine hits.

WASM SIMD and exception handling got across the line in late 2022 and 2023 respectively. SIMD matters for the software paths in the engine (decompression, vector math). Exception handling matters because the Source codebase uses C++ exceptions in ways that previously had to be `-fno-exceptions`-compiled or stubbed.

Community reaction split usefully. The "WASM is finally ready" camp missed that WASM has been ready; the substrate around it shipped last. The "why didn't they use WebGPU" camp missed that WebGPU still isn't available in Safari stable as of mid-2026, and any browser-first game project that wants more than Chrome-on-Windows reach is still on WebGL2.

What this means for your stack

If you're shipping anything that wants threads in WASM, the cross-origin isolation requirement is non-negotiable and it will break your existing deployment. You need to set COOP/COEP on your origin, which means every third-party script and asset (analytics, ad networks, CDN-hosted images, embedded YouTube) either needs CORP headers or has to be loaded through a credentialless iframe. Most ad networks still don't set CORP. This is why "just compile our app to WASM" is rarely a one-week project for anything with embeds.

If you're building anything that needs to persist large blobs client-side — ML model weights, video editor projects, offline-first document stores — OPFS should be your default, not IndexedDB. The performance delta on bulk reads is roughly an order of magnitude, and the API is small enough (`navigator.storage.getDirectory()` and a few methods) that you don't need a wrapper library. The catch: it's worker-only for the synchronous access handles, which means your storage layer has to be architecturally split.

For the cloud-gaming-versus-local-WASM question: the slqnt.dev approach costs Valve nothing per session and runs offline after first load. Cloud gaming costs roughly $0.50-2.00 per hour in GPU time and dies the moment the connection drops. For any game whose engine is small enough to compile to WASM and whose assets fit in the OPFS quota (typically a few GB), local-in-browser is now the better economic model. Cloud gaming's remaining moat is rendering games whose engines or assets don't fit those constraints — which, for now, is anything from roughly 2018 onward at AAA scale.

Looking ahead

The ceiling for this approach is roughly the last engine generation before bindless rendering, mesh shaders, and ray tracing became load-bearing — call it the 2015-era AAA back catalog plus everything indie. Expect a wave of source-available engines (idTech, Build, GoldSrc, early Unity-LTS games) to get the same treatment over the next 12 months, mostly as game-preservation projects. The interesting policy question isn't technical: it's whether publishers treat browser-runtime ports of their lapsed-but-still-IP titles the way they currently treat ROM sites, or the way Sega eventually treated Steam re-releases. The substrate is ready. The licensing protocol around it isn't.

Hacker News 651 pts 263 comments

Half-Life 2 in a Browser

→ read on Hacker News
modeless · Hacker News

And Quake 3: https://thelongestyard.link/q3a-demo/And Unreal Tournament: https://dos.zone/mp/?lobby=utThere's also https://noclip.website/ which, while not playable, has hundreds of levels from dozens of older games that you can explore fre

mrtksn · Hacker News

Interesting, I am not able to play HL2 on Steam because macOS no longer has 32-bit support and Valve never compiled if for 64-bit but here we are, it’s playable on the same OS in the browser.BTW IIRC there was some method to convert the 32-bit game binaries to make them run on recent macs. I remembe

memoryuns4f3fff · Hacker News

Here is a link to the blog post since I didn’t see it mentionedhttps://www.slqnt.dev/blog/hl2-in-web

utopiah · Hacker News

That's also the kind of Website, beside the impressive technical result, that reminds me nothing can be blocked.It's not about bypassing VPN or deep pack inspection, rather it's about how once anything, including a very complex video game (like here) to an entire OS with a host machin

hwc · Hacker News

With WASM and WebGL being mature technologies, I'm not sure why there aren't more video games published this way. For really big games with lots of assets, having those assets in local storage makes sense. But I wouldn't mind if a game "installer" is just your browser asking

// share this

// get daily digest

Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.