A browser-based pedestrian evacuation simulator. Sketch a room on a grid, describe the emergency in plain language, and watch research-backed agents evacuate in real time using the Social Force Model.
The app walks you through four stages — Landing → Draw → Scenario → Simulate:
- Draw walls, exits, and obstacles on a grid canvas (or generate a room instantly with the Room Creator)
- Describe the scenario in plain language — an LLM classifies occupants into behavioural groups (children, staff, mobility-impaired, panicking, etc.) with research-backed movement parameters
- Simulate — agents auto-spawn inside the enclosed room and evacuate in real time, driven by Helbing's Social Force Model; speed shown by fill colour, group identity by ring colour
- Analyse — a persistent density heatmap highlights choke points (high density + low speed) after completion, and every run is auto-saved to a history panel
| Feature | Details |
|---|---|
| Stage-based workflow | Landing → Draw → Scenario → Simulate, each with a dedicated layout |
| Grid editor | Click/drag to paint walls, exits, obstacles; right-click to erase |
| Room Creator | Press R — modal with width × height in metres, live preview, centres the room on the grid |
| Straight-line drawing | Hold Shift while dragging — locks to the dominant axis, shows a live length tooltip |
| Undo | Ctrl+Z — up to 25 steps, one undo unit per stroke |
| Scenario AI | Plain-language description → LLM classifies groups → research-backed SFM parameters; preset chips (🔥 Office / 🏥 Hospital / 🎉 Nightclub) for instant examples |
| Per-group physics | Each agent carries individual desired_speed, tau, radius from research profiles |
| Dual agent encoding | Fill colour = speed (blue→red); ring colour = group identity |
| Auto-spawn | Agents appear only inside the enclosed room — no manual placement |
| Social Force Model | Vectorised NumPy physics: driving force + agent repulsion + bilinear wall repulsion |
| Flow fields | Multi-source BFS from exits; O(1) direction lookup per agent per tick |
| Stuck fix | Flow-field kick after 20 low-speed ticks; prevents corner deadlocks |
| Density heatmap | Live low-opacity overlay during the run; freezes into a high-contrast traffic-light scale with choke-point borders on completion |
| History panel | Saved layouts and auto-saved SIM cards (with heatmap thumbnail) in one place |
| Speed control | Sim Speed pills: ¼× ½× 1× 2× 4× |
| Completion banner | Compact, non-blocking green bar on the canvas when a run finishes |
Two terminals required.
# Terminal 1 — backend
cd backend
python -m venv venv
venv\Scripts\activate # Windows PowerShell: .\venv\Scripts\Activate.ps1
pip install -r requirements.txt
uvicorn main:app --reload # http://127.0.0.1:8000
# Terminal 2 — frontend
cd frontend
npm install
npm run dev # http://localhost:5173Open http://localhost:5173.
Add your Anthropic API key to .env to enable Scenario AI:
LLM_API_KEY=sk-ant-...
LLM_MODEL=claude-haiku-4-5-20251001
- Landing — click → Begin Designing to enter the editor
- Draw — select a tool (
1–4) or click a toolbar pill button; click/drag to paint walls, exits, obstacles; pressRto open the Room Creator, holdShiftwhile dragging for straight lines,Ctrl+Zto undo. At least one exit is required to continue - Scenario — choose a preset chip or type a custom description, click ✦ Analyse — the LLM fills in the group table and suggested agent count; adjust the count, then click → Run Simulation
- Simulate — click ▶ Play (or
Space) to start; ⏸ Pause, ↺ Reset (R), or pick a speed pill; after completion the heatmap freezes and a SIM card is auto-saved to the history panel; ← Edit Plan returns to Draw
| Key | Action |
|---|---|
1 |
Wall tool |
2 |
Exit tool |
3 |
Obstacle tool |
4 |
Eraser |
R |
Room Creator (draw stage) / Reset simulation (simulate stage) |
Shift + drag |
Straight-line paint (locks to dominant axis) |
Ctrl+Z |
Undo last stroke (draw stage) |
Space |
Play / Pause |
| Right-click | Erase cell |
Browser (Vite + Vanilla JS)
│
│ HTTP (REST) — submit grid, control simulation, parse scenario
│ WebSocket — agent positions streamed at ~30 fps
▼
FastAPI (Python)
├── Grid model grid.py
├── Flow field (BFS) flow_field.py
├── Social Force Model social_force.py (NumPy vectorised)
└── LLM classifier scenario.py (Anthropic API)
F_total = F_drive + Σ F_agent_repulsion + Σ F_wall_repulsion
- Driving —
mass × (v_desired − v_actual) / τ - Agent repulsion —
A_soc × exp((r_sum − dist) / B_soc) - Wall repulsion — bilinear-interpolated EDT gradient; agents are hard-clamped clear of walls after integration
- Integration — Euler, dt = 0.05 s; velocity capped at 3.0 cells/s
- Exit detection — agent removed within 1.2 cells of any exit centre
POST /api/scenario/parse sends the description to Claude with a system prompt specifying the exact JSON schema, and returns { context, event, panic_level, interpretation, suggested_agents, suggested_speed, groups[] }. Fractions are normalised server-side to sum to 1.0, and the frontend auto-fills the agent count and matching speed pill.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/grid |
Submit layout + groups; returns initial agent snapshot |
| POST | /api/sim/start |
Start / resume simulation |
| POST | /api/sim/pause |
Pause simulation |
| POST | /api/sim/reset |
Reset to initial agent positions |
| POST | /api/sim/speed |
Set speed multiplier {value: 0.25–8} |
| POST | /api/scenario/parse |
Parse plain-text description → classified groups |
| WS | /ws/sim |
Stream agent frames at ~30 fps |
Agent-Room/
├── backend/
│ ├── simulation/
│ │ ├── grid.py # Grid dataclass, cell-type constants
│ │ ├── flow_field.py # Multi-source BFS → per-cell direction vectors
│ │ ├── social_force.py # SFM physics loop, per-agent params
│ │ └── profiles.json # Research-backed movement profiles + scenario presets
│ ├── api/
│ │ ├── routes.py # REST endpoints
│ │ ├── scenario.py # LLM scenario parsing endpoint
│ │ └── websocket.py # WS broadcast loop (~30 fps)
│ ├── llm/
│ │ └── scenario_parser.py # Alternative profile-based parser (not active in current UI)
│ ├── state.py # SimState singleton
│ ├── main.py # FastAPI app + CORS + lifespan
│ └── requirements.txt
│
├── frontend/
│ ├── src/
│ │ ├── config.js # Grid dimensions, cell types, colours
│ │ ├── state/AppState.js # Single source of truth: grid, tool, agents, groups, undo stack
│ │ ├── canvas/
│ │ │ ├── GridEditor.js # Paint cells; straight-line + undo + stage guard
│ │ │ └── Renderer.js # Grid, heatmap, agents, overlays
│ │ ├── ui/
│ │ │ ├── LandingAnim.js # Landing page dot animation
│ │ │ ├── Toolbar.js # Floating tool pill + room creator
│ │ │ ├── ControlPanel.js # Scenario AI panel
│ │ │ ├── StatsPanel.js # Live counters + status dot
│ │ │ └── HistoryPanel.js # Saved layouts + auto-saved SIM cards
│ │ ├── network/SimSocket.js # WebSocket client + auto-reconnect
│ │ └── main.js # Stage manager, keyboard shortcuts, event wiring
│ ├── index.html # Landing + app + room creator modal
│ ├── vite.config.js # Dev proxy /api /ws → backend
│ └── package.json
│
├── .env.example # Config template
└── .gitignore
Copy .env.example to .env:
BACKEND_HOST=127.0.0.1
BACKEND_PORT=8000
GRID_COLS=40
GRID_ROWS=30
CELL_SIZE=20
ALLOWED_ORIGIN=http://localhost:5173
# LLM Scenario Engine
LLM_API_KEY= # Anthropic API key
LLM_MODEL=claude-haiku-4-5-20251001
GRID_COLS in .env is a fallback — the frontend computes columns dynamically to fill the available canvas width.
| Layer | Choice |
|---|---|
| Frontend | Vite + Vanilla JS (no framework) |
| Rendering | HTML5 Canvas 2D |
| Backend | FastAPI + Uvicorn |
| Simulation | NumPy (vectorised) |
| Pathfinding | BFS floor fields |
| Real-time | WebSocket |
| LLM | Anthropic Claude (Haiku) |