About › Technology
What City Defense runs on: the technology
City Defense is a pure web game: the map, the 3D city, enemy pathfinding, the simulation and the bot test all run in the player's browser. The server only delivers static files. The game is built on MapLibre GL JS (map), three.js (3D), OpenFreeMap vector tiles made from OpenStreetMap data, TypeScript and Vite.
City Defense is free to play in your browser – no download, no sign-up. First mission: Brno, náměstí Svobody.
Play freeTechnology at a glance
| Part | Technology | What it does |
|---|---|---|
| Map | MapLibre GL JS 6 | vector map, camera, tile loading, attribution |
| Map data | OpenStreetMap → OpenMapTiles schema → hosted by OpenFreeMap (Liberty style) | streets, buildings with heights, land cover, water |
| 3D graphics | three.js (WebGL 2) | buildings, ground, trees, rubble, characters, towers and effects |
| Particles | three.quarks | smoke, explosions and dust from collapses |
| 3D models | glTF (.glb) with meshoptimizer compression, prepared with glTF Transform | theme characters and towers (CC0) |
| Textures | KTX2 / Basis Universal | photo textures for the realistic city style (CC0) |
| Code | TypeScript, Vite 8, no framework | the whole game as a single web app |
| Parallel work | Web Workers | building the 3D city and ground, bot testing of missions |
| Place search | Photon (komoot) | address search in the mission editor |
| Tests | Vitest | over a thousand automated tests, regression runs on real map tiles |
| Hosting | Cloudflare Workers (static assets) | serving the game files from a CDN |
The map: MapLibre and vector tiles
The map is drawn by MapLibre GL JS, an open-source library for vector maps in the browser. The base map is the Liberty style from OpenFreeMap, which serves vector tiles in the OpenMapTiles schema built from OpenStreetMap data. The most detailed tiles are at zoom level 14.
Nothing is downloaded in advance. The game reads streets, buildings, land cover and water from the tiles the map has already loaded for display (via querySourceFeatures). It hides shop and restaurant icons from the style so the map stays clean and the fight stays readable.
A game world built from map data
- The street network is a graph weighted by road class. Shortest paths to the gates of the defended area come from a multi-source Dijkstra search. When a building collapses, routes are recomputed.
- The height map has 1 m cells storing heights in decimeters. When a building is damaged, only its bounding rectangle is updated.
- Tower line of sight is a ray cast over the height map (a grid traversal after Amanatides and Woo). When a tower is built, the game precomputes a visibility table every 1 m along the streets in range; during play it only looks values up.
- Ballistics use a game gravity of 30 m/s², a closed-form elevation angle and collision checks of the parabola against facades and roofs.
- The simulation runs at a fixed step of 1/60 s with seeded randomness. The same moves give the same game, so tests and bots are reproducible.
3D in the same canvas as the map
three.js draws into the same WebGL context as MapLibre, as custom map layers: the ground, the city with its trees, and the characters. Because they share one depth buffer, buildings correctly hide enemies – and an enemy behind a building shows up as a colored silhouette (an “X-ray”).
- Only the play area plus 60 m is in 3D; the rest of the map stays flat. In Brno that is about 1,100 buildings.
- Building shadows come from a static 4096 × 4096 shadow map (2048 × 2048 on touch devices) that is only redrawn when something changes.
- The stylized style draws facades, windows floor by floor, roofs and damage with shaders and no textures.
- The realistic style uses CC0 photo textures and windows drawn in code. It picks plaster and roof colors by region (7 regions, from Central Europe to East Asia) based on the mission's coordinates.
Web Workers: heavy lifting off the main thread
City geometry, ruins, rubble heaps and the ground surface mask (1 m cells, 1,400 × 1,400 in Brno) are built in a Web Worker, so the game doesn't stutter while loading. The mission bot test runs in workers too: on a computer with four or more cores, both bots play at the same time.
KTX2 textures
The realistic style uses five texture arrays in KTX2 format (Basis Universal): 11 plasters and walls, 3 high-rise facades, 7 roof coverings, 3 kinds of rubble and 11 ground surfaces – 3.24 MB in total. They are only downloaded if you choose the realistic style, and the graphics card receives them in a format it supports. The transcoder ships with the game; nothing is loaded from third-party CDNs.
Hosting: static files, computation on the player's side
The game is a static site on Cloudflare Workers at citydefense.app. The server computes nothing – the simulation, routing, 3D and bots all run in the player's browser. Only two outside services are called from the browser: OpenFreeMap for map tiles and, in the editor, Photon for place search. There are no accounts and no database of our own; missions are saved in the browser and shared by link.
Tests
The pure game logic is covered by more than a thousand automated tests in Vitest. Regression tests run on four real map tiles of Brno kept in the repository (street network, buildings, ground surfaces). A balance test plays the mission with computer bots and checks that the difficulty stays in the agreed range. Shaders are validated with glslang.
Frequently asked questions
Do I need a powerful computer?
A browser with WebGL 2 is enough. High quality is the default even on a phone; if the game stutters on a slower device, it lowers sharpness and shadows by itself and offers low quality between waves. Low quality can also be chosen on the start screen.
Why does a mission take a moment to load?
The game first waits for the map tiles, then builds the street network, buildings, height map and 3D city from them. The realistic style also downloads 3.24 MB of textures.