角頭械鬥 · Turf War: Taipei — a round-based SWAT vs Militia browser shooter set in Taipei. TypeScript, Three.js and SpacetimeDB multiplayer; play free, nothing to install.
See the codeA round-based SWAT vs Militia shooter set in the streets of Taipei — free in your browser, nothing to install.
Play · Trailer · 中文預告片 · Gameplay guide · Contributing
Click the preview to watch the 30-second trailer.
Turf War: Taipei is an open-source, browser-first team shooter. Two teams of up to 24 fight short, one-life rounds across Taipei — Ximending's neon streets, an office floor 383 m up Taipei 101, the Chiang Kai-shek Memorial Hall — with cash, a store and weapon attachments in the spirit of the classic browser shooter BeGone. It runs on TypeScript and Three.js in the browser and a server-authoritative SpacetimeDB module online, with the same simulation powering offline play.
Gameplay
Multiplayer
Accessibility and platforms
![]() Taipei 101 · 88F: the damper hall in the tower's core | ![]() Ximending: a firefight on Cinema Street (電影街) |
![]() Memorial Hall (中正紀念堂): the grand staircase from Liberty Square | ![]() Vehicles: the helicopter over Ximending (cars and scooters too) |
![]() Store: weapons and attachments with BeGone's prices and stats | ![]() Lobby: Quick play, Start a server, Join a server |
![]() 繁體中文: every screen in Traditional Chinese (炸彈已安裝) |
Prerequisites: Bun 1.3.10 (use bun / bunx, never npm or npx). For local
online play, also the SpacetimeDB CLI 2.10.2.
git clone https://github.com/madeyexz/turfwar.git
cd turfwar
bun install --frozen-lockfile
bun install --cwd spacetimedb --frozen-lockfile
bun run dev # http://localhost:5173 — Solo and Practice work right away
Online play on your machine — in a second terminal:
bun run dev:spacetime # local SpacetimeDB on :3000 with the module published as "turfwar"
VITE_SPACETIMEDB_URI=same-origin VITE_SPACETIMEDB_DATABASE=turfwar bun run dev
Checks (the same ones CI runs on every push and pull request):
bun run test # Vitest (not `bun test`)
bun run build # type-check and production build
bun run typecheck:module # the SpacetimeDB server module
shared/ Simulation shared by the browser and the server: movement, collision,
maps/ weapons, vehicles, bots, rounds and the twelve maps
match/ Match state, economy, rooms, the packed per-tick frame and the tick itself
spacetimedb/ The SpacetimeDB server module: tables, reducers, admin views, scheduled tick
src/ The browser game: rendering, HUD, input, audio, lobby, online client
module_bindings/ Generated client bindings for the module
public/ Models, textures, sounds, fonts, icons and the PWA manifest
admin/ privacy/ The owner's dashboard and the privacy page
deploy/ The self-hosted SpacetimeDB container and its release scripts
tools/ Asset import and conversion tools (their own dependencies)
docs/ Gameplay, architecture and development guides
shared/ and
run unchanged in the browser (Solo, Practice, client prediction) and in the server module.The full design — validation, performance budgets and known trade-offs — is in docs/ARCHITECTURE.md.
Ideas the project would welcome help with (see known limitations):
Contributions are welcome — bug reports, maps, balance, translations and code alike.
dev and open your pull request against dev (never main).Please follow the Code of Conduct. Bugs and ideas go to GitHub issues.
If you enjoy the game, ⭐ the repository and share it. Hosting is paid out of pocket — to sponsor the servers, email ian@rippling.computer.
The code is released under the MIT License. Third-party assets keep their own licences (CC0 and the SIL Open Font License). The names 角頭械鬥 and Turf War: Taipei and the logo are not licensed for reuse — forks are welcome under a different name.
No proprietary game assets are used: the weapon and mode names follow BeGone, but every model, texture, sound and line of code here is original or CC0/OFL.
tools/import-assets.ts (textures resized to WebP, animation tracks pruned and
meshopt-compressed); see public/assets/LICENSE.txt.tools/import-guns.ts
(bun tools/import-guns.ts, with the pack extracted under GUN_SRC; --preview <dir> renders
measured side views for placing grips, sights and muzzles) into public/assets/guns.glb.tools/fetch-sounds.ts downloads both, cuts the takes, trims, limits, normalizes and encodes
them to mono MP3 in public/assets/sfx (~570 KB); see public/assets/sfx/LICENSE.txt. In game
(src/audio.ts) each shot layers the recording with a synthesized crack and low thump, the
recorded action, an outdoor tail and casing bounces; remote shots are delayed by distance and
crossfade to the distant take, and suppressed shots are muffled.tools/fetch-textures.ts; see
public/assets/tex/LICENSE.txt.public/fonts/OFL.txt).public/fonts/NotoSansTC-OFL.txt), from google/fonts. tools/subset-font.ts (bun tools/subset-font.ts;
needs uv, or Python with fontTools and brotli) cuts the variable font to weights 500–900 and to the
characters the game uses (every Chinese character in src/, shared/ and index.html, Taiwan's 4,808
common characters from tools/font/edu-standard-4808.txt, CJK punctuation, Bopomofo and full-width forms):
public/fonts/noto-sans-tc.woff2, about 1.4 MB and 5,084 glyphs, loaded only for Chinese text
(unicode-range). src/ui/font.test.ts fails when the source uses a character the font lacks.tools/import-taipei.ts into shared/maps/taipei-data.ts. Its shop names are that game's own parody
brands. Its district meshes and atlas, street furniture, prop meshes (scooter, YouBike, trees,
plants, signal heads) and skyline are that game's too, exported by the same tool.tools/import-xinyi.ts
into shared/maps/xinyi-data.ts. Its mall and shop names are that game's own parodies. The mall's
interior, the sunken garden and the curtain-wall detailing are this project's own.spacetimedb npm package declares ISC
but ships the SpacetimeDB Business Source License 1.1 text; the SpacetimeDB server/CLI is BSL 1.1.
Review those terms before any commercial self-hosting.The asset tools live in tools/ with their own package.json; the source packs themselves are not
committed.
角頭械鬥 · Turf War: Taipei — a round-based SWAT vs Militia browser shooter set in Taipei. TypeScript, Three.js and SpacetimeDB multiplayer; play free, nothing to install.
See the codeA round-based SWAT vs Militia shooter set in the streets of Taipei — free in your browser, nothing to install.
Play · Trailer · 中文預告片 · Gameplay guide · Contributing
Click the preview to watch the 30-second trailer.
Turf War: Taipei is an open-source, browser-first team shooter. Two teams of up to 24 fight short, one-life rounds across Taipei — Ximending's neon streets, an office floor 383 m up Taipei 101, the Chiang Kai-shek Memorial Hall — with cash, a store and weapon attachments in the spirit of the classic browser shooter BeGone. It runs on TypeScript and Three.js in the browser and a server-authoritative SpacetimeDB module online, with the same simulation powering offline play.
Gameplay
Multiplayer
Accessibility and platforms
![]() Taipei 101 · 88F: the damper hall in the tower's core | ![]() Ximending: a firefight on Cinema Street (電影街) |
![]() Memorial Hall (中正紀念堂): the grand staircase from Liberty Square | ![]() Vehicles: the helicopter over Ximending (cars and scooters too) |
![]() Store: weapons and attachments with BeGone's prices and stats | ![]() Lobby: Quick play, Start a server, Join a server |
![]() 繁體中文: every screen in Traditional Chinese (炸彈已安裝) |
Prerequisites: Bun 1.3.10 (use bun / bunx, never npm or npx). For local
online play, also the SpacetimeDB CLI 2.10.2.
git clone https://github.com/madeyexz/turfwar.git
cd turfwar
bun install --frozen-lockfile
bun install --cwd spacetimedb --frozen-lockfile
bun run dev # http://localhost:5173 — Solo and Practice work right away
Online play on your machine — in a second terminal:
bun run dev:spacetime # local SpacetimeDB on :3000 with the module published as "turfwar"
VITE_SPACETIMEDB_URI=same-origin VITE_SPACETIMEDB_DATABASE=turfwar bun run dev
Checks (the same ones CI runs on every push and pull request):
bun run test # Vitest (not `bun test`)
bun run build # type-check and production build
bun run typecheck:module # the SpacetimeDB server module
shared/ Simulation shared by the browser and the server: movement, collision,
maps/ weapons, vehicles, bots, rounds and the twelve maps
match/ Match state, economy, rooms, the packed per-tick frame and the tick itself
spacetimedb/ The SpacetimeDB server module: tables, reducers, admin views, scheduled tick
src/ The browser game: rendering, HUD, input, audio, lobby, online client
module_bindings/ Generated client bindings for the module
public/ Models, textures, sounds, fonts, icons and the PWA manifest
admin/ privacy/ The owner's dashboard and the privacy page
deploy/ The self-hosted SpacetimeDB container and its release scripts
tools/ Asset import and conversion tools (their own dependencies)
docs/ Gameplay, architecture and development guides
shared/ and
run unchanged in the browser (Solo, Practice, client prediction) and in the server module.The full design — validation, performance budgets and known trade-offs — is in docs/ARCHITECTURE.md.
Ideas the project would welcome help with (see known limitations):
Contributions are welcome — bug reports, maps, balance, translations and code alike.
dev and open your pull request against dev (never main).Please follow the Code of Conduct. Bugs and ideas go to GitHub issues.
If you enjoy the game, ⭐ the repository and share it. Hosting is paid out of pocket — to sponsor the servers, email ian@rippling.computer.
The code is released under the MIT License. Third-party assets keep their own licences (CC0 and the SIL Open Font License). The names 角頭械鬥 and Turf War: Taipei and the logo are not licensed for reuse — forks are welcome under a different name.
No proprietary game assets are used: the weapon and mode names follow BeGone, but every model, texture, sound and line of code here is original or CC0/OFL.
tools/import-assets.ts (textures resized to WebP, animation tracks pruned and
meshopt-compressed); see public/assets/LICENSE.txt.tools/import-guns.ts
(bun tools/import-guns.ts, with the pack extracted under GUN_SRC; --preview <dir> renders
measured side views for placing grips, sights and muzzles) into public/assets/guns.glb.tools/fetch-sounds.ts downloads both, cuts the takes, trims, limits, normalizes and encodes
them to mono MP3 in public/assets/sfx (~570 KB); see public/assets/sfx/LICENSE.txt. In game
(src/audio.ts) each shot layers the recording with a synthesized crack and low thump, the
recorded action, an outdoor tail and casing bounces; remote shots are delayed by distance and
crossfade to the distant take, and suppressed shots are muffled.tools/fetch-textures.ts; see
public/assets/tex/LICENSE.txt.public/fonts/OFL.txt).public/fonts/NotoSansTC-OFL.txt), from google/fonts. tools/subset-font.ts (bun tools/subset-font.ts;
needs uv, or Python with fontTools and brotli) cuts the variable font to weights 500–900 and to the
characters the game uses (every Chinese character in src/, shared/ and index.html, Taiwan's 4,808
common characters from tools/font/edu-standard-4808.txt, CJK punctuation, Bopomofo and full-width forms):
public/fonts/noto-sans-tc.woff2, about 1.4 MB and 5,084 glyphs, loaded only for Chinese text
(unicode-range). src/ui/font.test.ts fails when the source uses a character the font lacks.tools/import-taipei.ts into shared/maps/taipei-data.ts. Its shop names are that game's own parody
brands. Its district meshes and atlas, street furniture, prop meshes (scooter, YouBike, trees,
plants, signal heads) and skyline are that game's too, exported by the same tool.tools/import-xinyi.ts
into shared/maps/xinyi-data.ts. Its mall and shop names are that game's own parodies. The mall's
interior, the sunken garden and the curtain-wall detailing are this project's own.spacetimedb npm package declares ISC
but ships the SpacetimeDB Business Source License 1.1 text; the SpacetimeDB server/CLI is BSL 1.1.
Review those terms before any commercial self-hosting.The asset tools live in tools/ with their own package.json; the source packs themselves are not
committed.