PlayGTA5

Explainer

How WebAssembly works, and how it runs GTA V in a browser

A browser tab isn't where you'd expect a huge 3D game to live. WebAssembly is why it can. This guide covers what it is, how a module goes from source code to running machine code, and how PlayGTA5 puts the pieces together.

About 10 minutes to read. The first half needs no programming background.

What WebAssembly actually is

WebAssembly, or wasm, is a binary format that every major browser can run. It became a W3C standard in 2019, so it is officially a grown-up web technology now, sitting right next to HTML, CSS and JavaScript.

Nobody writes it by hand, thankfully. You write C, C++ or Rust, a compiler turns that into a .wasm file, and the browser runs the result inside a sandboxed virtual machine.

Short version: code written for desktop programs can run in a web page, safely, at close to native speed.

Why browsers needed it

JavaScript is flexible, and you pay for that flexibility. The browser downloads text, parses it, guesses types while the code runs, then re-optimises when a guess turns out wrong. That is fine for menus and forms. It is not great for a game engine that has to hit a frame deadline sixty times a second.

WebAssembly takes most of the guessing out:

  • Compact. A binary file is smaller than the same program as text and quicker to decode.
  • Typed. Every value is a number of a known type, so nothing gets guessed at run time.
  • Pre-optimised. The compiler already did the heavy lifting before the file reaches you.
  • Predictable. No garbage collector pauses, so frame times stay steadier.

From source code to a running module

A wasm module goes through a short, strict routine before it does anything.

  1. Compile. A toolchain like Emscripten (built on LLVM) turns C or C++ into a .wasm file plus a little JavaScript glue.
  2. Download. The browser fetches it like any other file, so it can be cached and served from a CDN.
  3. Validate. The browser checks that the module is well formed and type correct. If it isn't, it never runs.
  4. Compile again. The engine translates the module for your actual CPU. Browsers can start while the file is still downloading, and many use a quick first pass followed by an optimising pass for hot code.
  5. Instantiate. The module is linked to the functions it imports from the page and given its memory. After that you can call its exports.

In JavaScript, loading one looks like this:

const { instance } = await WebAssembly.instantiateStreaming(
  fetch("engine.wasm"),
  { env: { log: (value) => console.log(value) } }  // imports the module may call
);

instance.exports.start();                            // exports the page may call

How it executes

Wasm defines a simple stack machine. Instructions push and pop values, so "push 2, push 3, add" leaves 5 on the stack. There are four basic number types, i32 and i64 integers and f32 and f64 floats, plus a 128 bit v128 for SIMD.

Control flow is structured: blocks, loops and branches to labelled blocks, and no jumping to a random address. That limit is what makes validation fast and stops a module from wandering out of its own code.

ConceptWhat it is
ModuleThe compiled unit: functions, memory, tables, imports and exports.
InstanceA module that has been linked and given its own state.
FunctionTyped code that takes numbers in and gives numbers back.
Linear memoryOne resizable block of bytes the module reads and writes.
TableA list of function references, used for indirect calls and virtual functions.
Import / exportThe only doors between the module and the outside world.

Memory, and why it can't escape

A module never sees your computer's memory. It gets linear memory: one flat array of bytes that grows in 64 KiB pages. C++ pointers become offsets into that array, and reading past the end traps instead of touching anything else.

The same rule covers everything else. A module can't open a file, make a socket or draw a pixel by itself. It can only call functions the page handed it. So the page decides what the module may do, and the module follows the same rules as the rest of the page.

Threads, SIMD and speed

Games want more than one core. WebAssembly borrows threads from the web platform instead of inventing its own:

  • Web Workers run in parallel, and each can host a module instance.
  • SharedArrayBuffer lets workers share memory, and Atomics give them safe locks and counters.
  • Cross-origin isolation is required for shared memory, so the server has to send Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers. PlayGTA5's server does.
  • SIMD instructions handle several numbers per instruction, which helps with animation, physics and image work.

With all that in place, well written WebAssembly usually lands within a small factor of the same program running natively. How small depends on the workload, the browser and your hardware, so be suspicious of anyone quoting one exact number.

Talking to the browser

A sealed module can't do anything useful alone, so everything goes through imports. Emscripten generates glue that maps familiar desktop ideas onto web APIs:

A game needsThe web provides
GraphicsWebGL, or WebGPU for modern GPU access and compute
Files and savesA virtual file system backed by browser storage (IndexedDB, OPFS)
AudioWeb Audio
InputKeyboard, mouse, touch, Pointer Lock and the Gamepad API
Networkingfetch, WebSocket and WebRTC

How GTA V fits in

Here is how this build is put together.

  1. The engine is a WebAssembly bundle. A compiled .wasm engine, its glue and packed shader files add up to roughly 200 MB, downloaded once and cached.
  2. Game data is streamed. Roughly 16 GB of archives are fetched in ranges, and an IO worker asks only for the blocks the game needs next.
  3. The browser keeps a block cache. Downloaded blocks live in the Origin Private File System (OPFS), which is why later launches start far faster.
  4. A separate worker renders with WebGPU. Draw calls from the engine turn into GPU work using shaders written in WGSL.
  5. The page stays responsive. Input, touch buttons, audio and settings run on the main thread while the heavy work happens in workers.

Why the first launch is slow: the browser has to download the engine, compile it and fill the block cache. Later launches skip most of that.

Where it falls over

  • It needs WebGPU. Use a current Chromium browser. chrome://gpu tells you whether WebGPU is available.
  • Hardware acceleration must be on. With software rendering the engine can't start.
  • Memory is finite. A tab has a ceiling for WebAssembly memory, which is why the low-spec preset and a lower render scale matter on small devices.
  • Keep the tab visible. Browsers throttle background tabs, which slows streaming and rendering.
  • Use the device check. The launcher's Check this device tool tests the browser, GPU, memory and storage and tells you which one fails.

FAQ

What is WebAssembly?

WebAssembly (wasm) is a compact binary instruction format and a sandboxed virtual machine built into every major browser. It lets code written in languages like C++ and Rust run on the web at close to native speed.

Is WebAssembly faster than JavaScript?

For heavy numeric work such as physics, audio and rendering, usually yes, because the code is already compiled, typed and predictable. For ordinary page logic JavaScript is just as good, and the two are designed to be used together.

Is WebAssembly safe?

Yes. A module is validated before it runs, can only touch its own linear memory, and has no access to files, the network or the operating system except through functions the host page explicitly hands it.

Why does the game need WebGPU as well?

WebAssembly only runs code on the CPU. Drawing frames needs the graphics card, which the browser exposes through WebGPU.

Why do some browsers fail to run it?

The engine relies on WebAssembly threads, shared memory and WebGPU. Browsers or devices without WebGPU, or with hardware acceleration turned off, cannot start it.

See it for yourself

Run the device check, launch a session and watch the WebAssembly engine start in your own browser.