Metropolis is an interactive 3D city simulation built as a single HTML file using Three.js. Users can fly through a living city and observe urban life including vehicles, pedestrians, weather, and random events.
metropolis/
├── metropolis-3d-city.html # The entire application (single file ~4700 lines)
└── README.md # Project documentation
The entire application is contained in metropolis-3d-city.html:
- HTML structure (lines 1-77): Canvas, HUD, minimap, loading screen, controls overlay
- CSS styles (lines 7-56): Embedded in
<style>tag - JavaScript (lines 78-end): ES module using Three.js from CDN
All systems follow a make/update pattern: mk*() functions create objects, upd*() functions update them each frame.
| System | Create Function | Update Function | Description |
|---|---|---|---|
| City Layout | mkCityLayout() |
- | 4x4 grid of roads, sidewalks, crosswalks |
| Buildings | mkBuildings(), mkBuilding() |
updWindowLights() |
Dynamic buildings with flickering windows |
| Traffic Lights | mkTrafficLights(), mkTL() |
updTL() |
4-way intersection signals |
| Cars | mkCars(), mkCar() |
updCars() |
Regular vehicles obeying traffic |
| Emergency | mkEmergencyVehicles() |
updEmergencyLights() |
Police, fire, ambulance |
| Buses | mkBuses(), mkDoubleDecker(), mkJitney() |
- | Tourist and jitney buses |
| Omnibus | createOmnibusLine(), mkOmnibus() |
updOmnibus() |
Fixed route with boarding |
| Pedestrians | mkPeds(), mkPed() |
updPeds() |
Citizens with state machine |
| Bicycles | mkBicycles(), mkBicycle() |
updBicycles() |
Cyclists that ignore lights |
| Weather | - | updWeather(), updRain(), updStormDebris() |
Sunny/cloudy/rain/storm |
| Special Events | Various create*() |
updSpecialEvents(), updateEvent() |
Random city events |
| Secrets | mkSecrets(), mkUFO(), mkBalloon(), mkHelicopter() |
updSecrets() |
Hidden discoverable objects |
| Billboards | mkBillboards() |
updBillboards() |
Animated rooftop signs |
const GRID = {
cellSize: 24, // Size of each city block
roadWidth: 8, // Width of roads
sidewalkWidth: 2, // Width of sidewalks
gridCount: 4 // 4x4 blocks
};
const CFG = { cars: 25, emergencyVehicles: 3, peds: 50, bikes: 4 };scene,cam,renderer,clock- Three.js core objectsbuildings[],cars[],peds[],tlights[]- Entity arraysweatherState- Current weather: 'sunny', 'cloudy', 'rain', 'storm'specialEvent- Current active random eventomnibus,omnibusStops[]- Bus line system
The animate() function (line 4012) runs the game loop:
- Handle camera movement from keyboard input
- Call all update functions in sequence
- Render the scene
Pedestrians (peds) use a state-based behavior system:
walking- Moving along sidewalkscrossing- Crossing street at crosswalkwaitToCross- Waiting for safe crossingenteringBuilding/insideBuilding/exitingBuilding- Building interactiontoBusStop/waitingForBus/onBus- Omnibus passenger behavior
- Cars check traffic lights, pedestrians, other vehicles, and obstacles
- Emergency vehicles can ignore red lights but stop for pedestrians
- Vehicles attempt lane changes to avoid obstacles
- Traffic lights cycle on 10-second phases
Random events trigger periodically (see triggerRandomEvent()):
parade- Floats, clown car, marching bandsemiTruck- Wide load convoyiceCreamTruck- Attracts pedestriansdogWalker- Person walking dogsstreaker- Police chaseweddingCortege- Wedding processionbrokenDownCar- Blocks traffic
Standalone systems:
- Garbage truck with "suction" mechanic
- Falling person event with ambulance response
Weather changes automatically or via 'R' key:
- Sunny - Clear skies, bright lighting
- Cloudy - Overcast, light wind
- Rain - Particle system (6000 drops)
- Storm - Heavy rain, lightning flashes, flying debris
| Key | Action |
|---|---|
| WASD / Arrows | Move |
| Q / E | Up / Down |
| Mouse | Look around |
| Scroll | Zoom (adjust FOV) |
| Space | Speed boost |
| R | Cycle weather |
| Click | Pointer lock |
- Create a
mk[VehicleType]()function that returns a THREE.Group - Add vehicle-specific userData properties (speed, type, etc.)
- Call
placeCarOnRoad()to position it - Push to
cars[]array and add to scene - Handle special behavior in
updCars()if needed
- Create
create[EventName]()function to spawn entities - Create
mk[EntityType]()for event-specific objects - Add case to
triggerRandomEvent()switch - Handle updates in
updateEvent()if needed - Clean up in
endEvent()
- Add new state string to the state machine
- Handle state transitions in
setNewPedTarget() - Add state-specific behavior in
updPeds()
- Function names use camelCase with prefixes:
mk(make),upd(update) - Entity groups store metadata in
userDataproperty - Colors are hex integers (e.g.,
0xff3333) - Positions use Three.js Vector3 coordinates
- The $ helper function is shorthand for
getElementById
- Shadow maps: 2048x2048 resolution
- Fog: starts at 80 units, full at 350 units
- Rain particles: 6000 in rain, more in storm
- Window toggle rate: throttled to avoid excessive updates
- All entities share common materials where possible
- Three.js v0.160.0 (loaded from CDN via import map)
- No build tools or package manager required
Open metropolis-3d-city.html directly in a modern browser. No server required for basic testing (though some browsers may require a local server for ES modules).
# Quick start
open metropolis-3d-city.html
# Or with a local server
python -m http.server 8000
# Then visit http://localhost:8000/metropolis-3d-city.html| Goal | Location |
|---|---|
| Change city size | GRID.gridCount |
| Add more vehicles | CFG.cars |
| New car colors | cols array in mkCars() |
| Billboard messages | brands array in mkBillboards() |
| Building heights | types array in mkBuildings() |
| Weather timing | nextWeatherChange calculation |
| Event frequency | eventCooldown and random checks |