A round-based SWAT vs Militia shooter set in the streets of Taipei — free in your browser, nothing to install.
- About
- Features
- Screenshots
- Getting started
- Project structure
- How it works
- Roadmap
- Contributing
- Security and privacy
- Support the project
- Acknowledgements
- License
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
- Two modes: Elimination and Sabotage (Militia arms the bomb at site A or B; SWAT defends or disarms). First to 10 rounds, one life per round, a 4 s freeze and 20 s of buy time.
- Thirteen maps: Ximending (西門町), Taipei 101 · Xinyi, Taipei 101 · 88F with its tuned mass damper, Memorial Hall (中正紀念堂), Sanchong (三重) across the river, homages to BeGone's maps and original battlefields — see Maps.
- Vehicles: cars and taxis with handbrake drifts, scooters you can shoot from, and a helicopter.
- Economy: cash for kills, headshots, assists and objectives; five primaries, two secondaries, grenades, and optics, suppressors, lasers and more (weapons).
Multiplayer
- Rooms of every size: 1v1, 6v6 and 24v24. Quick play joins the fullest open room, Start a server opens a public room or a private one with a 4-letter code, and Join a server lists every live room with its ping.
- Bots fill empty slots, online and offline, at three difficulties.
- Server-authoritative: damage, cash, purchases, rounds and the bomb run on the server; movement is client-predicted and server-validated.
Accessibility and platforms
- English and 繁體中文, switchable at any time.
- Rebindable controls for every action, using physical key positions (AZERTY and Dvorak work).
- Phones and tablets: touch controls with an editable layout, hold-to-aim firing, and an installable app (PWA) — see Phones and tablets.
- Solo and practice need no server at all.
![]() Taipei 101 · 88F: the damper hall in the tower's core |
![]() Ximending: storefronts and covered streets |
![]() Memorial Hall (中正紀念堂): the grand staircase from Liberty Square |
![]() Xinyi: the plaza beside Taipei 101 |
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 awayOnline 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 devChecks (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 moduleshared/ Simulation shared by the browser and the server: movement, collision,
maps/ weapons, vehicles, bots, rounds and the thirteen 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
about/ en/about/ The static about pages (繁體中文, English) with the FAQ, for search and AI answers (docs/SEO.md)
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
- One simulation, two places. Movement, collision, weapons, bots and rounds live in
shared/and run unchanged in the browser (Solo, Practice, client prediction) and in the server module. - The server decides. Players' reducers only queue input; a 30 Hz scheduled tick applies it with the shared rules and publishes one packed frame per room, which clients interpolate.
- Validated movement. Clients predict their own movement and report it; the server checks every report against speed, collision and vehicle rules and corrects anything out of bounds.
- Always on. The game server (Taipei) never sleeps; if the lobby cannot reach it, it says so after 30 seconds and offers Retry.
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):
- Lag compensation (server-side rewind) for hit validation
- Interest management, so clients receive only nearby soldiers
- More Taipei maps, and authored reload animations
- Skill-based matchmaking, vote kick and clans
- Testing and polish on Safari and Firefox
Contributions are welcome — bug reports, maps, balance, translations and code alike.
- Read CONTRIBUTING.md for setup, the checks and the rules of the codebase.
- Branch from
devand open your pull request againstdev(nevermain). - Make sure CI is green, and include screenshots for anything visual.
Please follow the Code of Conduct. Bugs and ideas go to GitHub issues.
- Security: please report vulnerabilities privately as described in SECURITY.md, not in a public issue.
- Privacy: no accounts and no ads. The game keeps your settings and an anonymous ID in your browser, gameplay stats on the game server, and uses anonymous analytics. The full statement is on the privacy page (source).
If you enjoy the game, ⭐ the repository and share it. Hosting is paid out of pocket — to sponsor the servers, email ian@rippling.computer.
- BeGone by nPlay — the rules, numbers and maps this game pays homage to.
- Quaternius, Poly Haven and the authors of The Free Firearm Sound Library — CC0 models, textures and sounds.
- Three.js, SpacetimeDB, Vite and Bun.
- Built largely with Claude (Claude Opus 5.5).
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.
The TW app icon was generated with Amp Painter. Its source is tools/app-icon.png;
run bun tools/make-icons.ts (after installing tools/ dependencies) to regenerate the browser
and home-screen sizes.
An approved, hand-traced SVG version is also available: eight editable vector paths with flat colors and no embedded raster image. It follows the same logo reuse restriction above. The icon generator still uses the original PNG; adding the SVG does not change the shipped icons.
For use without the black tile, download the transparent SVG or its 1024px PNG export. Both keep the same emblem and logo reuse restriction; the white W works best on dark backgrounds. These variants do not replace the app icons.
Asset credits and licences
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.
- Characters, animations, props and modular kit pieces/trim sheets: free “Standard”
editions of Quaternius' Universal Base Characters, Universal Animation Library 1 & 2,
Sci-Fi Essentials Kit and Modular Sci-Fi MegaKit — CC0 1.0 (https://quaternius.com).
Converted by
tools/import-assets.ts(textures resized to WebP, animation tracks pruned and meshopt-compressed); seepublic/assets/LICENSE.txt. - Soldiers: Quaternius' Ultimate Modular Men (the SWAT operator and six street-gang irregulars with
their idle, walk, run and death animations) — CC0 1.0 (https://quaternius.com/packs/ultimatemodularcharacters.html).
Converted by
tools/import-modular.tsintopublic/assets/soldiers.glbandsoldier-anims.glb;?soldiers=classicbrings back the Universal Base Characters soldier. - Weapons: models from Quaternius' Ultimate Gun Pack (a bayonet for the knife) — CC0 1.0
(https://opengameart.org/content/low-poly-guns-pack). Converted from OBJ by
tools/import-guns.ts(bun tools/import-guns.ts, with the pack extracted underGUN_SRC;--preview <dir>renders measured side views for placing grips, sights and muzzles) intopublic/assets/guns.glb. - Gunshots: recordings from The Free Firearm Sound Library by Ben Jaszczak, Brian Nelson, Kevin Heras and Matthew Nanney — CC0 1.0 (https://opengameart.org/content/the-free-firearm-sound-library). Each firearm plays two to four separate near-distance takes round-robin (M9A1: Walther PPQ, MP7: PPSh, MP5: Carl Gustav M45, M4A1: AR-15, M249: AK-47, M1014: Benelli Nova and Winchester Model 12, M110: Tikka T3 and Springfield 1917 .30-06), with a mid-distance take per class as the distant layer.
- Reload and handling foley, all CC0 1.0 on OpenGameArt: SpringySpringo's Gun Reload Sounds, BMacZero's Gun Reload Sound Effects, zer0_sol's Handgun Reload Sound Effect and Shotgun Reload Sound Effects, and LFA's Equipment Clicks III.
tools/fetch-sounds.tsdownloads both, cuts the takes, trims, limits, normalizes and encodes them to mono MP3 inpublic/assets/sfx(~570 KB); seepublic/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.- Terrain and surface textures (including the brick, plank, corrugated-iron, plaster and
cobblestone architecture of the BeGone maps): Poly Haven, CC0 1.0, fetched by
tools/fetch-textures.ts; seepublic/assets/tex/LICENSE.txt. - Font: Rajdhani by Indian Type Foundry, SIL Open Font License 1.1 (
public/fonts/OFL.txt). - Font: Oswald (Latin, the lobby) by the Oswald Project Authors, SIL Open Font License 1.1 (
public/fonts/Oswald-OFL.txt). - Chinese font: Noto Sans TC by Google and Adobe, SIL Open Font License 1.1
(
public/fonts/NotoSansTC-OFL.txt), from google/fonts.tools/subset-font.ts(bun tools/subset-font.ts; needsuv, or Python with fontTools and brotli) cuts the variable font to weights 500–900 and to the characters the game uses (every Chinese character insrc/,shared/andindex.html, Taiwan's 4,808 common characters fromtools/font/edu-standard-4808.txt, CJK punctuation, Bopomofo and full-width forms), in two files:public/fonts/noto-sans-tc.woff2(the game's own characters and the punctuation, about 280 KB), loaded only for Chinese text (unicode-range), andpublic/fonts/noto-sans-tc-extra.woff2(the rest of the common characters, about 1.1 MB), loaded only when a name or chat line needs one.src/ui/font.test.tsfails when the source uses a character the first file lacks. - The Taipei maps (Ximending, Taipei 101 · Xinyi, Taipei 101 · 88F), the Taipei 101 outline and the
other city maps' layouts are this project's own, drawn from public knowledge of the real places
(
shared/maps/ximending*.ts,xinyi*.ts,taipei101.ts,tower101.ts); the city past each map is a procedural skyline (src/render/cityscape.ts); the props (scooters, trees, plants) and cars are built in code (src/render/props.ts,dressing.ts). No data from another game or from a map database is used. - Sky, terrain, architecture, effects and the remaining audio (knife, casings, footsteps, explosions, heartbeat, bomb, round and cash cues, UI, and the fallback weapon voices used before the recordings load) are procedural.
- Engine/libraries: Three.js (MIT), Vite (MIT). The
spacetimedbnpm 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.



