Quake III Arena rebuilt for the web: WebAssembly client, dedicated server, and supporting services all living in one repo.
Q3JS compiles ioquake3 to WebAssembly, streams the original pak assets through a modern React front end, tunnels UDP
traffic through a WebSocket proxy, and keeps server metadata in a Quarkus backend. You can jump straight in at
q3js.com, or read on to see how the pieces fit together and how to work on each one locally.
- Browser-native Quake 3 – the
game/module buildsioquake3with Emscripten 4.0.19 and serves it through the Vite/TanStack app inwebsite/. - Dedicated server + WS bridge – the
server/module buildsioq3dedand exposes it to browsers via a Node-based WebSocket↔UDP proxy. - Master/API service – the Quarkus service listens for heartbeats on /api/servers/heartbeat, and exposes a REST API.
- Single workspace – scripts, Dockerfiles, and helper tooling live alongside the code so you can spin up the entire stack with a few commands.
| Path | Description |
|---|---|
game/ |
Emscripten build scripts that compile ioquake3 into ioquake3.{js,wasm}. |
server/ |
Native dedicated server build, Dockerfile, entrypoint, and WebSocket↔UDP proxy. |
master/ |
Quarkus app (REST master server) |
website/ |
Vite + React + TanStack Router UI that embeds the WASM build and server picker. |
emsdk/ |
Local Emscripten SDK checkout used by game/build.sh. |
ioq3/ |
Submodule pointing to the ioquake3 source code. |
Browser (React + ioquake3.wasm)
│
├── HTTP(S) → Quarkus Service (REST /api/servers)
└── WebSocket → ws-udp-proxy → UDP → ioq3ded (maps, rcon, gameplay)
| Tool | Version / Notes |
|---|---|
| Node.js & npm | Node 18+ (Node 20.x recommended) for the website and ws proxy. |
| Java | JDK 21 for Quarkus. |
| Maven | Included via master/mvnw, but installing Maven 3.9+ helps. |
| Docker + Docker Compose | Required for the Postgres dev DB and optional server builds. |
| CMake + build-essential | Needed to compile the native server locally. |
| Emscripten SDK | 4.0.19 (included via emsdk/; run git submodule update --init emsdk or download manually). |
| Quake III Arena assets | Copy your legal pak*.pk3 files into baseq3/ and baseq3.zip. |
Legal notice: The
pakfiles are free, shareware demo versions of Quake III Arena. To play the full game, you must own a legal copy of Quake III Arena or Quake III: Team Arena and copy the correspondingpakfiles from your installation.
-
Acquire assets
- Copy the baseq3 folder into
website/public/so the browser client can download them.
- Copy the baseq3 folder into
-
Build the WebAssembly client
pushd game ./build.sh # installs/activates emsdk 4.0.19 and compiles ioquake3 popd
The output
game/build/Release/ioquake3.{js,wasm}must be copied (or symlinked) intowebsite/src/lib/. The script already patches OpenGL shaders for WebGL 2 / GLES precision requirements. -
Run the Quarkus service
pushd master ./mvnw quarkus:dev # REST API on http://localhost:8080, UDP master on :27950 popd
Test the API:
curl http://localhost:8080/api/servers. -
Build & run the dedicated server + proxy
pushd server ./build.sh # cmake build of ioq3ded in server/build/Release ./entrypoint.sh # launches ioq3ded with the default cvars/maps popd
- The proxy listens on
WS_PORT(default27961) and points toQ3_HOST:Q3_PORT(default127.0.0.1:27960). Override via env vars when runningentrypoint.shor the Docker container.
- The proxy listens on
-
Run the web UI
pushd website npm install npm run dev # Vite dev server on http://localhost:3000 popd
The SPA polls the REST API for server data and opens the WebSocket proxy when you click “Play”.
game/build.shbootstraps Emscripten, patches GLSL shaders for precision qualifiers, and builds a Release target with SDL2, WebGL2, and filesystem support (-sFORCE_FILESYSTEM=1 -lidbfs.js).- The generated artifacts are consumed by the React app (
website/src/lib/ioquake3.jsand.wasm). Persistent data lives in IDBFS;GamePage.tsxhandles mounting/syncing and versioned cache invalidation. - Tooling: Vite + Tailwind CSS, TanStack Router, TanStack Query, shadcn/ui, Vitest, and Biome for formatting/linting.
Use
npm run test,npm run lint,npm run format,npm run check.
server/build.shconfiguresioq3ded(headless server, QVMs enabled) via CMake/GCC, copiesbaseq3/into the build output, and leaves binaries inserver/build/Release/.ws-udp-proxy/index.jsconverts browser WebSocket traffic to raw UDP packets understood by the Quake server. Environment variables:Q3_HOST,Q3_PORT– native server address (defaults127.0.0.1:27960).WS_PORT– listen port for browsers (27961).RCON_PASS– optional; enables the “kick ping 999” watchdog (matchesrconPasswordinentrypoint.sh).POLL_MS,RESP_TIMEOUT_MS,CONSEC_REQUIRED– tune heartbeat/kick behaviour.
entrypoint.shlaunches both the proxy andioq3dedwith sensible defaults (dedicated server,q3dm17). Use the multi-stageserver/Dockerfileif you prefer container builds:docker build -t q3js/server ./server.
- Built with Quarkus 3.29 (Jakarta EE 10 APIs), exposes
GET /api/serversreturningServerResponseDTOs. - Tests:
./mvnw test. Native builds:./mvnw package -Dnative.
- Server:
docker build -t q3js/server ./server && docker run --rm -p 27960:27960/udp -p 27961:27961 q3js/server. Mountbaseq3/if you do not bake assets into the image. - Website:
docker build -t q3js/website ./website(Dockerfile provided), or deploy the staticdist/folder produced bynpm run build.
- Want to customize game types, maps, or run a persistent instance? Follow the hosted guide at q3js.com/guide for detailed steps on provisioning assets, configuring the Quarkus master, and exposing the WebSocket proxy.
- The guide walks through the Docker-based deployments.
| Command | Purpose |
|---|---|
game/build.sh |
Full WebAssembly rebuild (deletes game/build/). Accepts EMSDK_ROOT, BASEQ3_SRC, BUILD_DIR, WEB_PORT. |
server/build.sh |
Native server rebuild. Set BASEQ3_SRC/BUILD_DIR to override defaults. |
- Browser shows black screen: Ensure
website/src/lib/ioquake3.{js,wasm}matches the latest build and that the files are referenced by Vite (restartnpm run devafter copying). emsdk_env.sh not found: SetEMSDK_ROOT=/path/to/emsdkbefore runninggame/build.sh.
- Engine source is derived from ioquake3 (GPLv2). See
ioq3/. - Game assets remain © id Software / Bethesda. Supply your own
baseq3data. - New code in this repository is licensed under the same terms as the respective sub-projects unless noted otherwise.
- The WebSocket proxy and integration glue were developed by lklacar; please provide attribution if you reuse these components.
- The
q3js.comwebsite and its original content are likewise authored and owned by lklacar; include proper credit when referencing or republishing any portion of it.
Happy fragging! Feel free to open issues or PRs with improvements to the stack.