---
title: "Building from source"
description: "Build the ja2-web engine yourself: the repository layout, the Emscripten 6.0.1 and Rust nightly toolchain, the dev server, and the flags that matter."
book: "Developers"
order: 60
---
This page covers what is in the ja2-web repository, running a development build, and
building the engine with Emscripten. To run your own instance without building anything,
see [Self-hosting](
/ja2/docs/self-hosting/).
## What's in the repository
This is a code-only repo. Large binaries (game data, dependency caches, build
artifacts) are excluded via
[`.gitignore`](
https://github.com/Virtastic/ja2-web/blob/main/.gitignore) and must be
provided or rebuilt locally.
| Path | Purpose |
|------|---------|
| `source-ja2/` | JA2 Stracciatella engine source (upstream v0.22.1 plus local WASM changes under `#ifdef __EMSCRIPTEN__`) |
| `wasm-build/` | Toolchain pin (`env.sh`) + configure/build/package scripts, the CDP test harness, smoke tests |
| `fsroot/` | Small preloaded tree baked into `ja2.data`: `externalized/`, `mods/` |
| `play/` | Browser front-end: `launcher.html`, `index.html`, `server.py` dev server, built `ja2.{js,wasm,data}` |
| `infra/` | `nginx.conf` (COOP/COEP + gzip_static) and Terraform (DNS) for production |
### Not included (kept local)
- `gamedata-src/`, `gamedata-lean/`, `fsroot/gamedata/`, `play/ja2*.data`: commercial
JA2 game data (`Data/*.slf`). Never committed - copyright.
- `build-wasm/` and all `*.wasm` / `*.data` build outputs.
- Prebuilt desktop SDL2 binaries and bulky community mods (get them from upstream
Stracciatella if you need them).
## Running (dev)
You need your own JA2 game data to build the bundled dev package. Then:
```bash
# 1. one-time: stage your JA2 Data/ and repackage the data blob
wasm-build/bundle-data.sh ~/path/to/ja2install
# 2. build the engine
wasm-build/configure-ja2.sh # emcmake configure
. wasm-build/env.sh && ninja -C build-wasm ja2
# 3. serve play/ over a cross-origin-isolated origin
python3 play/server.py # http://localhost:8795 (set PORT= to change)
```
The dev server sets the cross-origin-isolation headers SharedArrayBuffer needs. Open the
URL in desktop Chrome. Set `OPENMW_LAUNCHER=0` to boot straight into the game instead of
the data chooser.
## Building
You need **Emscripten 6.0.1**, a **Rust nightly** toolchain, CMake, and Ninja. The exact
pins live in [`wasm-build/env.sh`](
https://github.com/Virtastic/ja2-web/blob/main/wasm-build/env.sh)
(a mismatched Rust nightly miscompiles `std::path` on `wasm32-unknown-emscripten` - do not
drift off the pin).
```bash
wasm-build/configure-ja2.sh # emcmake cmake -S source-ja2 -B build-wasm
. wasm-build/env.sh && ninja -C build-wasm ja2
cp build-wasm/ja2.{js,wasm,data} play/
```
Things to watch, which is why the flags are scripted in `env.sh`:
- The whole build uses `-fexceptions` (JS exceptions, the rustc emscripten ABI) and
`-pthread`. Do **not** add `-flto`.
- `-sSTACK_SIZE=8388608` (8 MB) - the world-map loader overflows the default 64 KB wasm
stack.
- `-msimd128` (C++ and Rust `+simd128`) vectorizes the software blitters.
- Audio defaults to a Wasm AudioWorklet backend; `?audio=legacy` selects the old
SDL/ScriptProcessor path.
[WebAssembly adaptations](
/ja2/docs/wasm-adaptations/) is a writeup of how the native
desktop engine was made to run in a browser tab.
## Hosting what you built
Serve `play/` over HTTPS (`http://localhost` also counts) with these headers on every
response, so the page is cross-origin isolated (required for SharedArrayBuffer /
pthreads):
```
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: cross-origin
```
Serve the precompressed `.gz` siblings with `Content-Encoding: gzip` (nginx
`gzip_static on;`). That takes the ~10 MB wasm down to ~3 MB over the wire (the `.js`
loader and small preloaded `.data` blob compress too). The reference vhost is
[`infra/nginx.conf`](
https://github.com/Virtastic/ja2-web/blob/main/infra/nginx.conf). The
engine files are content-versioned into `e/<hash>/`, so a new build is served at a fresh
URL and the CDN can never serve a mismatched build.
Pull requests are welcome; see
[CONTRIBUTING.md](
https://github.com/Virtastic/ja2-web/blob/main/CONTRIBUTING.md).
Deployment and CI are maintainer-only.