Skip to content

Repository files navigation

πŸ›‘οΈ TANKS

A browser team tank battle in the spirit of Battle City β€” destructible terrain, power-ups, and a flag you have to defend.

Play now

TypeScript PixiJS PeerJS Vite No backend License

A team match on the Classic map

Blue against red, as many tanks a side as the map puts spawn points. Bring friends over a 6-character room code, hand a second player the arrow keys on your own machine, or take on a team of bots β€” every slot nobody claims is filled by one. Runs in any modern browser, desktop or phone, with no server, no install and no account.

Status: playable prototype. A full local match works today: menu β†’ room β†’ a live match with terrain, bonuses, flags and bots, plus a level editor and a headless test suite (npm test). Multiplayer (PeerJS) is implemented but not yet verified across two real devices over the internet; the touch controls are verified in an emulated phone browser, not yet on real hardware. SPEC.md indexes the authoritative design; Β§13 Plan lists what's left.

Highlights

🧩 Teams sized by the map The roster is the map's spawn count, not a fixed number β€” a 1v1 test arena and a 16-a-side brawl are the same engine, and every free slot is filled by a bot
βš”οΈ Team battle with an objective Destroy the enemy flag to take the round, or grind the enemy out of respawns before the clock runs out
πŸ† Played as a series A match is best-of: rounds run back to back on rebuilt terrain, and the first team to the room's win target (1–10, default 5) takes it. No draws β€” a round the clock ran out on goes to whoever has lives left, or got deepest into the enemy base
πŸ€– Bots on every free slot Easy / Medium / Hard / Extreme, set per bot or for the whole roster β€” genuinely different tactics, not just better aim
🧱 Destructible terrain Brick crumbles cell by cell, steel resists until you're upgraded, forest conceals, water stops tanks but not bullets, ice slides, sand slows
✨ Eight power-ups Four are yours, four swing the whole team
🌐 Peer-to-peer multiplayer Join by code or invite link over WebRTC β€” no server to run, no public IP, no accounts
πŸ”– A room code you can keep Name your room (SERGEY, DVOR) instead of dictating six characters, and the same invite link works every evening
πŸ•ΉοΈ Two players, one keyboard WASD and the arrow keys, same team or opposite ones
πŸ—ΊοΈ Six maps + a level editor Paint your own arena, validate it, play it β€” and it travels to your guests over the wire
πŸ“± Phone-ready Split-screen touch controls and a real fullscreen mode, the whole arena on screen

Gallery

Main menu
Main menu β€” create, join, edit, play
Room screen
Room β€” claim a slot, live map + spawn preview, per-bot difficulty
Map library
Map library β€” built-ins and your own maps, side by side
Level editor
Level editor β€” paint terrain, place flags and spawns, resize, test play
Touch controls on a phone
On a phone β€” a stick appears under whichever thumb lands on the movement half; tap anywhere on the other half to fire

Power-ups

Bonuses drop on the battlefield now and then β€” drive over one to take it. While a bonus is on you, its icon spins around your tank in that bonus's colour, so everyone can see what you picked up.

The eight power-ups

HELMET STAR SPEED MINE are yours alone; SHOVEL CLOCK GRENADE RESPAWN swing your whole team. GRENADE is lobbed at the biggest knot of enemies and takes out whoever is still inside its ring when it lands β€” at most half their team, with a blast that grows with the map.

How a match goes

  1. Create a room β€” pick your nickname, the map and the difficulty its bots start at. You get a code and an invite link. Leave the code field empty for a random one, or type your own (3–12 letters or digits): it's remembered, so the link you send your friends keeps working.
  2. Set up the match β€” rounds to win, round time limit, respawns, bonuses per minute and friendly fire are all in the room itself, so you can retune them while people arrive; everyone sees the change.
  3. Take a slot β€” click any slot on either team; the preview marks the spawn you would get. Friends join with the code; bots hold everything nobody claimed.
  4. Fight β€” the host's browser simulates everything and streams snapshots to the guests at 20 Hz.
  5. Take the round β€” blow up the enemy flag, or outlast them on respawns. The score goes up over the frozen battlefield, and three seconds later the next round starts on the same map with the walls rebuilt and the clock back to full.
  6. Win the match β€” first team to the room's win target. The result screen states the series score (BLUE WINS 5:2), the full match scoreboard, and a way straight back to the room.

Controls

Action Player 1 Player 2 (same machine)
Move W A S D Arrow keys
Fire 1 N
Drop mine (needs the MINE bonus) 2 M
Scoreboard hold Tab
Menu / pause Esc

Hold two direction keys at once to drive diagonally. In the menus, Enter triggers the screen's primary button. Esc opens the in-match menu β€” playing on your own that really pauses, with the simulation stopping dead and picking up where it left off; with other players connected the match can't be frozen, so it's just a menu over a running game.

On a phone or tablet β€” the battlefield is split in half and each half is a control
  • Movement half (left by default) β€” put a thumb down anywhere in it and drag. A stick appears under your thumb, wherever that is, and the tank drives that way in any of 8 directions. Lift to stop. Drag far and the stick follows you, so you never run out of travel or have to re-grab.
  • Fire half (right by default) β€” tap anywhere to fire, hold to keep firing. The tank shoots where it faces, so there's nothing to aim and no button to find.
  • MINE sits in the outer top corner, the only fixed button β€” out of the way of the firing thumb.
  • Left-handed? Settings β†’ Movement stick side swaps the two halves and the MINE button with them.
  • The top bar carries the three buttons a phone needs and a keyboard doesn't: scoreboard, fullscreen, and the pause menu.

Landscape only β€” portrait shows a rotate prompt.

Fullscreen is how it's meant to be played on a phone: browser chrome eats a fifth of a landscape screen and its show/hide animation resizes the arena mid-fight. Starting a match on a touch device goes fullscreen and asks for a landscape orientation lock automatically (Settings β†’ Fullscreen on match start, on by default). It's also a button in the match top bar and on the main menu β€” worth knowing, since a phone has no Esc to get back out with.

Maps and the level editor

Six maps ship with the game β€” classic, crossroads, swamp, thicket, fortress and iceworks β€” chosen by the host in the lobby and previewed live, with your spawn point highlighted, before the match starts. They don't all point the same way: some face the two bases top and bottom, thicket and iceworks put them left and right, and fortress buries them in opposite corners.

A map is a plain block of text and carries its own size and roster, anything from 2Γ—2 up to 128Γ—128, so the team size comes from the map rather than the other way round. Build your own in the Level Editor: paint terrain with a Paint-like palette, drag to fill rectangles, place flags, spawns and bonus points, resize the grid around any anchor, undo a gesture at a time, and hit Test play to drop straight into a match on it. A map is validated before it can be played, so an unplayable one never reaches a match. Built-in maps are read-only but can be copied. Custom maps live in localStorage, export and import as text, and travel to your guests over the wire when you host on one. Sign in with Google in Settings (optional) and your maps follow you to your other devices.

Playing together

One player creates a room and gets a code like K7QM2X (or an invite link ?room=K7QM2X). Everyone else joins with it, picks a slot on either team β€” the preview shows the map, your spawn point, and your tank before you commit β€” and hits ready. Any slot you don't fill is played by a bot, so the teams are always full whether there are two of you or ten. Clicking another slot moves you there; clicking your own hands it to a bot and makes you a spectator, and clicking any bot slot puts you back in.

The room creator hosts the match: their browser runs the authoritative simulation and all the bots. If someone drops, a bot takes their tank over for the rest of the match.

Your lobby setup sticks, too: Create Room reopens on the map you played last, and each map keeps its own time limit, respawn pool, bot difficulty and friendly-fire toggle.

Quick start

npm install
npm run dev        # http://localhost:5173/tanks/
npm run build      # type-check + production build into ./dist
npm run preview    # serve the build locally
npm run deploy     # publish ./dist to the gh-pages branch
npm run maps:check # validate the built-in maps in src/world/maps/
npm test           # headless suite β€” movement, map format, editor, bots

vite.config.ts sets base: "/tanks/", the repo subpath GitHub Pages serves from β€” which is also why npm run dev opens at http://localhost:5173/tanks/ rather than the bare root. Pushing to main deploys automatically via .github/workflows/deploy.yml; npm run deploy publishes from your machine instead. The build is a plain static site β€” ./dist/ can be hosted anywhere that serves it from /tanks/.

Optional cloud maps. Google sign-in and map sync need a Supabase project; without one the game simply has no cloud section. To enable it: run supabase/schema.sql in the project's SQL editor, enable the Google provider under Authentication, add http://localhost:5173/tanks/ and your Pages URL to the allowed redirect URLs, then put VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY in .env.local (see .env.example) and, for the automatic deploy, in the repo's Actions secrets. The anon key is public by design β€” row-level security is what protects the data; never use the service_role key.

Tech

TypeScript Β· React (menus, HUD) Β· PixiJS (WebGL rendering) Β· PeerJS (WebRTC DataChannel) Β· Vite Β· static hosting on GitHub Pages. No backend, and no shipped art β€” every texture, from the tanks to the power-up icons, is generated procedurally at boot.

Project structure
src/
  main.tsx           # entry: debug shortcut, ?room= deep link, or the main menu
  ui/                # React: screens (menu, create, join, room, match, result, settings,
                     # editor), the HUD, and the route value that says which one is up
  game/              # constants, config, settings, touch controls, the live menu backdrop
    constants.ts     # every tuning number, grouped: map, tank, shooting, bonuses, rounds, bots, net, editor
    config.ts        # ids and labels: teams, bot difficulties, the DEBUG switch
  world/             # the simulation β€” no rendering, no DOM, no network
    sim.ts           # authoritative tick: movement, bullets, bonuses, flags
    grid.ts bullet.ts bonus.ts flag.ts rules.ts tank.ts
    maps/            # map sources as text, parser/validator, editor model, custom-map library
  ai/                # bots: per-tank controller, pathfinder, team role planner
  net/               # RoomHost / RoomClient behind one RoomController interface
    peer.ts          # PeerJS plumbing; protocol.ts β€” wire messages
  render/            # PixiJS: arena, procedurally generated atlas, previews, bonus art
  util/              # input, audio, fullscreen, math, storage
tests/               # headless node:test suites
scripts/             # map validation and the test runner
docs/screenshots/    # the images in this README
Docs
  • SPEC.md β€” the authoritative design, an index over the section files in specs/.
  • specs/level-editor.md β€” the editor's own detail sheet.
  • AGENTS.md β€” working notes: current status, how to launch and verify the game.

Roadmap

Milestones M0 – M10 are laid out in Β§13 Plan.

  • M0–M6 β€” scaffold, terrain, tanks, match rules, bots, bonuses, screens + local co-op
  • M7 β€” multiplayer (PeerJS star topology, room code, host-authoritative sim) β€” implemented, not yet played across two real devices
  • M8 β€” mobile touch controls, fullscreen and the orientation gate β€” verified in an emulated phone browser, not yet on real hardware
  • Level editor β€” paint, validate, save, share; custom maps over the network
  • M9 β€” fortress and iceworks maps, real audio mixing pass, effects/kill-feed polish, balance
  • M10 β€” first playtest round

Deliberately not planned: host migration, dedicated servers, accounts and ranking, matchmaking, replays. See Β§15 Out of scope.

License

MIT.

Credits

Inspired by Battle City (Namco, 1985).

About

Browser team tank battle in the spirit of Battle City - destructible terrain, power-ups, bots, level editor, P2P play by room code; team size comes from the map

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages