> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Zombie Survival in the browser: Cairn's first web export

_26 October 2026 · Abdulkader Safi_

A game engine written in C++ on Vulkan should not run in a browser tab. Mine does now.

Zombie Survival, the top-down shooter that ships as a Cairn sample, is up on itch.io as a web game. No download and no installer. You open the page and click Play. This post is what it took to get there, what works, and what still doesn't.

**tl;dr:** Cairn's runtime compiles to WebAssembly and draws with WebGL2. The editor exports a game as a folder with `index.html` plus a zip that itch.io takes as it is. Zombie Survival's zip is 8.5 MB and runs in current Chrome, Edge, Firefox and Safari.

## Play it first

Hold the courtyard against zombie waves that grow each round. Kills earn points, and points open barricades to three more areas, each with a better gun.

<iframe frameborder="0" src="https://itch.io/embed/5236793?linkback=true&amp;bg_color=222222&amp;fg_color=eeeeee&amp;link_color=5b61fa&amp;border_color=363636" width="552" height="167" style="max-width: 100%"><a href="https://abdulkader-safi.itch.io/zombi-survival-web-export-test">Zombi-Survival-Web-export-test by Safi</a></iframe>

Controls:

- WASD to move, the mouse to aim, left click to shoot
- 1 to 4 or Q to switch guns, E to open a barricade, Esc to pause
- A gamepad works too: left stick moves, right stick aims, right trigger shoots
- Your best wave is saved in the browser, so it is still there next visit

The pistol never runs out. The SMG (750 points), shotgun (1,500) and rifle (2,500) need ammo that zombies drop. Brutes turn up at wave 4, and they take a while to put down.

## What is in the 8.5 MB

The export writes four files and zips them:

| File                 | Size   | What it is                                      |
| -------------------- | ------ | ----------------------------------------------- |
| `Cairn-Runtime.wasm` | 7.7 MB | The engine: renderer, physics, scripting, audio |
| `Cairn-Runtime.js`   | 147 KB | Emscripten's loader                             |
| `Game.cairnpak`      | 623 KB | Every file of the game in one pack              |
| `index.html`         | 7.7 KB | The page with the Play button                   |

The game itself is the small part. Almost everything is the engine, and it is the same 7.7 MB for every game you export. I expected the pack to dominate. It didn't: the zombies are cubes on one palette texture, and the six scripts are a few kilobytes of Lua.

## Why WebGL2 and not WebGPU

WebGPU is the obvious pick for a modern engine. I went the other way for one reason: it was missing from Firefox on Linux and on Intel Macs when I decided. WebGL2 is in every current browser.

What made it possible is that Cairn's renderer has no compute shaders. Every effect (shadows, ambient occlusion, bloom, FXAA) is a vertex and a fragment shader, which WebGL2 can run. The cost is CPU time per draw call. Instanced batches keep that down.

The engine renders through [nvrhi](https://github.com/NVIDIA-RTX/NVRHI), so I wrote a WebGL2 device behind nvrhi's interfaces. Not one render pass knows it is in a browser. The shaders are compiled to SPIR-V for Vulkan at build time, and SPIRV-Cross turns them into GLSL ES 3.0 for the web.

## The bug that turned the scene dark

The first frame in Chrome had the right shapes and almost no light.

Vulkan's viewport in nvrhi flips y. WebGL's doesn't. So every texture held its picture upside down compared with the desktop, and anything that reads a position back from a texture got the mirrored one. Shadows, ambient occlusion and sky light all read the wrong spot. The fix was to flip y in every translated vertex shader, as nvrhi's Vulkan viewport does, and swap which faces count as front. It took a long look at a dark courtyard to see it.

Other gaps WebGL2 has, and what the device does about each:

- **No base instance or base vertex.** Attributes start further into their buffers instead.
- **No program without a fragment shader.** Depth-only passes get an empty one.
- **No filtered read of a depth texture without comparison.** It is read unfiltered.
- **No threads.** Jolt physics runs its jobs on one thread, and assets load when first used.
- **No sound before a click.** Browsers keep a page silent until the player does something, which is why the game starts on the Play button.

GL calls are slow from WebAssembly, so the device only sets state when it changes.

## How fast is it

Zombie Survival in Chrome draws the same picture as the desktop runtime. Feature Tour, the heavier sample with six levels (one with about 10,000 particles, one with 48 skinned characters), finishes its whole benchmark at 100 frames a second in Chrome on my Mac, which is that display's cap.

One test on one machine. I would not promise more than that yet.

## Exporting your own game

In the editor, open the Export window and pick **Web**. Or send one command:

```json
{"command": "export", "output": "/path/to/Builds", "platform": "web"}
```

Upload the zip to itch.io as an HTML game and tick "This file will be played in the browser". [Exporting a game](/guide/editor/export.md) has the full steps, including the viewport size to set under itch.io's embed options.

## What is not there yet

- No GPU timings in the stats overlay on the web
- No automated test of the web build: it needs a browser, so I run Zombie Survival and Feature Tour in Chrome by hand after each renderer change
- Saves stay in that browser on that machine

Play the game, break it, and tell me what browser it failed on. That is the test I can't run myself.

## FAQ

### Can Cairn export games to the web?

Yes. Since 26 October 2026, Cairn exports a game for the browser as well as for the desktop. The export is a folder with an HTML page, the engine as WebAssembly and the game's files in one pack, plus a zip of those files for itch.io.

### Which browsers run a Cairn web game?

Any current Chrome, Edge, Firefox or Safari with WebGL2. The game draws with WebGL2, not WebGPU, because WebGL2 is in every current browser. If a browser has no WebGL2, the page says so instead of showing a blank canvas.

### How big is a Cairn web export?

Zombie Survival's zip is 8.5 MB. The engine itself is 7.7 MB of WebAssembly, and it is the same for every game. The game's own files are 623 KB, so a small game adds little on top of the engine.

### Do saved games work in the browser?

Yes. A web game keeps its saves and settings in the browser's storage for the site, so they last between visits. They do not move to another browser or another machine.
