Star Wars Jedi Knight II: Jedi Outcast, single-player, running in a browser tab.
▶ Play now · Discord · YouTube · Releases · Issues
A WebAssembly port of Raven Software's original 2002 GPL source release, compiled with Emscripten. It runs entirely on your machine: the engine is WebAssembly, the game data is read from your own disk or from the free demo, and nothing is uploaded or streamed. Ghoul2 skeletal characters, the saber, and Force powers all work; the campaign runs end to end.
The port is deliberately conservative — games/jk2/ is Raven's pristine drop, and every browser
adaptation is a separate reviewable commit on top, so git diff <import> -- games/jk2 is the
port. No code is copied from OpenJK or iortcw. The engineering log, including theories that
turned out to be wrong, is in docs/WASM_ADAPTATIONS.md.
Open https://jk2.virtastic.app in desktop Chrome, Chromium, Edge or Brave, then pick a route:
- Play the demo — the official free Kejim Post mission. Nothing to own, nothing to install.
- Bring your own copy — point the picker at the
GameData/basefolder of a legal install and select theassets*.pk3files (assets0,assets1,assets2,assets5). They are read straight from your disk; nothing is uploaded.
Saves and settings persist in the browser (IDBFS), independent of which data you loaded.
Windows tip: copy
GameDatasomewhere outsideProgram Filesfirst — browsers refuse to grant folder access inside protected system paths, and that is where Steam installs by default.
Single-player only. The multiplayer sources are part of the drop but are not built.
From a clean clone, with nothing but Docker installed:
docker build -t jk2-web . # compiles the engine from source
docker run --rm -p 8080:80 jk2-web # http://localhost:8080or simply:
docker compose up # http://localhost:8080The first build pulls the Emscripten toolchain image and compiles for a few minutes; after that
it is layer-cached. See SELF_HOSTING.md for serving it on a real server, and
for how to add the free demo pak so the demo route works on your own instance.
This repo is code only. No game data of any kind is committed.
| Path | What it is |
|---|---|
games/jk2/ |
Raven's pristine GPL drop + the __EMSCRIPTEN__-guarded port commits |
shared/wasm-build/ |
env.sh (the toolchain pin), the Emscripten platform layer, build scripts, CDP test harnesses |
shared/web/ |
server.py — the COOP/COEP dev server, with HTTP-Range for pk3 streaming |
play/jk2/ |
index.html (the game page) and launcher.html. Built .js/.wasm are gitignored |
cloud/ |
Optional Cloud Locker backend — OAuth sign-in plus S3 storage. Not required to play |
infra/ |
nginx.conf (the serving contract) and the Terraform DNS reference |
ci/ |
A reference Jenkins pipeline |
Retail .pk3 archives, the demo pak, build-jk2/, the generated jk2.js/.wasm/qagame.wasm,
and node_modules/. The .gitignore and .dockerignore are written so game data cannot be
committed or baked into an image by accident.
source shared/wasm-build/env.sh # Emscripten 6.0.1 — see env.sh for discovery
shared/wasm-build/build-jk2.sh # engine -> play/jk2/{jk2.js,jk2.wasm}
shared/wasm-build/build-jk2-modules.sh # game DLL -> play/jk2/qagame.wasm
python3 shared/web/server.py jk2 # dev server on :8793Both build scripts are needed: the engine alone boots to the menu, but loading a map requires the
game module (qagame.wasm), which is where single-player keeps game + cgame. They are separate on
purpose — qagame.wasm is an Emscripten side module that the engine dlopens per map,
mirroring the original's per-map DLL reload.
Emscripten 6.0.1, pinned in shared/wasm-build/env.sh — source it rather than hardcoding
flags. No -flto (it miscompiles the boot path). Single-threaded. Pure C++: no Rust, no
-pthread, so the toolchain is just emscripten/emsdk:6.0.1 with nothing added.
The harnesses in shared/wasm-build/*.mjs drive a real headless Chrome over CDP — they boot the
actual engine and assert on what it renders and logs, rather than on mocks:
node shared/wasm-build/map-sweep.mjs 8793 "<map1,map2,...>" # whole campaign, one session
node shared/wasm-build/verify-menu.mjs 8793 <map> # menus, incl. same-map reload
node shared/wasm-build/verify-transition.mjs 8793 <mapA> <mapB> # scripted level transition
node shared/wasm-build/verify-jk-save.mjs jk2 8793 "+devmap <map>" # save round-trip incl. reloadThe one hard requirement is the cross-origin isolation header set (COOP/COEP/CORP) plus correct
application/wasm and cache headers. infra/nginx.conf is the reference vhost, and
SELF_HOSTING.md has worked nginx and Caddy configs.
Desktop Chrome / Chromium / Edge / Brave. The folder picker uses the File System Access API, which Firefox and Safari do not implement; mobile is out of scope.
jk2-web is licensed under the GNU General Public License, version 2 — see
LICENSE and NOTICE.md.
GPLv2, not GPLv3. Raven's drop grants version 2 and does not include the customary "or (at your option) any later version" clause for Raven's own code, so the combined work cannot be relicensed forward.
- Engine code under
games/jk2/— Copyright (C) 2002 Activision / Raven Software, released by the copyright holder under GPLv2. - The port — the Emscripten platform layer, build scripts, the browser shell under
play/, the test harnesses, and the optional cloud backend — is likewise GPLv2. - Bundled third-party components keep their own licenses; see
THIRD-PARTY-LICENSES.md, which also records which of them are actually compiled into the WebAssembly binary.
STAR WARS and JEDI KNIGHT are trademarks of Lucasfilm Ltd. and/or its affiliates. Activision and Raven Software are trademarks of their respective owners. This project is not affiliated with, endorsed by, or sponsored by any of them; the names are used only to identify the software this is derived from.
No retail game data is included in, or distributed by, this repository. The retail archives are commercial content and are not redistributable — you must supply your own legally-obtained copy, or use the official free demo mission.
Read CONTRIBUTING.md first — it covers the two rules that matter most here:
games/jk2/ stays a pristine import with the port as separate commits, and claims about engine
behaviour need a measurement with a positive control, not a plausible story.
- Discord — the fastest place for help, screenshots and news.
- YouTube (@Virtastic-Apps) — demos, build logs, and the other native-to-browser ports we work on.
- GitHub Discussions — longer-form questions and showcase threads.
Security issues: please follow SECURITY.md rather than opening a public issue.
Raven Software and Activision, for releasing the engine sources under the GPL in the first place.
WASM port © 2025–2026 Virtastic. Built with the Claude Agent SDK.