Zaw Aung's Blog

Classic adventure games in the browser with ScummVM and WebAssembly

ScummVM is a free engine that runs classic point-and-click adventures, such as Beneath a Steel Sky, Flight of the Amazon Queen and Lure of the Temptress. This project compiles it to WebAssembly and serves it from my own server, so a game runs in a browser tab with nothing to install.

The key idea: the server only serves files

There is no game server. The container is plain nginx serving static files. The game itself runs entirely in the visitor’s browser, in WebAssembly:

browser ──HTTPS──► host nginx ──► container (nginx + basic auth)
                                   ├─ scummvm.html / .js / .wasm
                                   └─ data/games/<game>   (the game files)

Saves and settings go to the browser’s IndexedDB, so they belong to one browser on one device, and the server stores nothing about players.

Building it

The Dockerfile has two stages. The first clones ScummVM and uses its own Emscripten build script to compile it to WebAssembly. It builds with --enable-plugins --default-dynamic, which makes each game engine a separate file that loads only when needed, so a visitor does not download engines for games they are not playing. The engine list is a build argument: fewer engines mean a faster build and a smaller download. The second stage copies the output into a small nginx image.

The first build is heavy: roughly 4 GB of RAM, 10 GB of disk and 30 to 60 minutes. After that it is cached.

Two details that make it work

A browser cannot list a directory over HTTP. ScummVM needs to know which files a game has, so at container start a script runs ScummVM’s own make_http_index.py, which writes an index.json into every folder. This is why the container has to be restarted after a new game is added.

WebAssembly needs the right MIME type. Browsers refuse to compile .wasm unless it is served as application/wasm, so the container’s nginx config declares that explicitly. The same config tells browsers to revalidate the app files (they are not content-hashed) but cache the large, rarely changing game data for a week.

Why it is behind a password

The whole site is behind HTTP basic auth. Game data keeps its own licence, and the project’s licensing is split: the wrapper files (Dockerfile, configs and scripts) are MIT, while ScummVM itself is GPLv3 and the built image contains it. The password file and the games folder are both git-ignored, so neither ends up in a public repository.

Things to know

Comments

Leave a comment

Comments are held for moderation before they appear.