Put a civic question in a living pixel town. Watch AI residents reason, talk, disagree, and change their minds.
Explore the live demo · Read the docs · Add a resident or scenario
Township is an open-source engine for making civic deliberation visible. A
scenario package defines the question, options, towns, fictional residents, local
context, and news beats. The Python engine runs a configurable sequence of
seed → converse → news → opinion → decide; a typed event stream brings every
walk, conversation, reaction, and opinion shift to a React + Phaser world.
It is designed for research, education, media literacy, and agent development, and it is deliberately easy to inspect: personas are Markdown, scenarios are JSON, runs are portable event logs, and a deterministic mock runs the whole pipeline without credentials.
Important
Township is a simulation, not a poll. Its outputs do not measure real public opinion and must never be presented as if they do. Residents are fictional composites and every output is an LLM artifact. Read Responsible Use before creating or publishing a real-world scenario.
- A world, not a spreadsheet. Residents follow routines through distinct towns, meet at local landmarks, speak in-world, react to news, and carry their changing stance into a district dashboard.
- Scenario-first by construction. Candidates, budget choices, places,
personas, and news live under
scenarios/<id>/—never hardcoded in the engine. - Zero-key from the first clone. The deterministic mock exercises every phase and powers offline tests. The Pages demo replays committed runs with no backend and no API calls.
- Live and replay share one path. Both publish the same discriminated event
union, so a saved run drives the same pixel town and charts as live inference —
and successful persistence leaves each run under
runs/as a portable event log, structured summary, and (when generation succeeds) narrative recap. - Bring the model you trust. Bedrock, Anthropic, OpenAI, OpenRouter, Ollama, LM Studio, and the built-in mock implement one provider contract.
- Human-readable agents. Each resident is one Markdown file with structured frontmatter, prose voice, routines, concerns, goals, and relationships.
Fictional composites authored as people rather than demographic rows, with portraits cropped from the sprite sheets that walk through the live town.
![]() Carlos Restrepo |
![]() Maria Santos |
![]() Miguel Hernandez |
![]() Rosa Chen |
![]() Jordan Williams |
![]() Vikram Iyer |
![]() Jen Russo |
![]() Mike Brennan |
![]() Frank DeLuca |
Requirements: Python 3.11+ and Node ^20.19.0 or >=22.12.0.
git clone https://github.com/StevenWang-CY/township.git
cd township
make install
SCENARIO=millbrook-budget make demoOpen localhost:8001. make demo builds and serves the
complete app with the deterministic mock provider: offline, free, and with no
.env file required. Use make dev for backend/frontend hot reload on ports
8001 and 5173. If uv is installed, make install
honors the committed lock exactly; pip remains the supported fallback.
Prefer the terminal?
township run --scenario millbrook-budget --provider mockThat command runs the full deliberation and, when the best-effort finalization steps
succeed, prints a recap and leaves a portable artifact in runs/<run_id>/. Useful
next commands:
township scenarios
township replay --demo --scenario nj11-2026The Python wheel is intentionally API/headless-only (CLI + scenario packages, no compiled web UI); use the Docker image or this source checkout for the full application. The hosted demo is a static replay player — zero network model calls, recorded runs clearly labeled; live chat, new simulations, voice, and God's View need a local backend.
| Surface | What it reveals |
|---|---|
| Living town | The landing view: routines, weather, time of day, conversations, gossip, gestures, and opinion ripples rendered in Phaser |
| District atlas | A storybook pixel overworld drawn from the towns' own tileset — vignettes, nameplates, and each town's cast and leading option |
| Resident chat | In-character conversation with private relationship context, trust, voice hooks, and a capability-protected personal journal |
| Dashboard | Cross-town patterns, issue fault lines, conversations, and stance trajectories |
| God's View | A transparent intervention sandbox for asking how agents react to a hypothetical development |
| Replay timeline | Pause, seek, change speed, or jump between rounds in a recorded run |
flowchart LR
P["Scenario package<br/>JSON + Markdown"] --> O["FastAPI orchestrator<br/>towns run in parallel"]
O <--> L["LLM provider<br/>or deterministic mock"]
O --> E["Typed event stream<br/>WebSocket + EventBus"]
E --> W["React + Phaser<br/>town, chat, dashboard"]
E --> R[("Portable run<br/>events + summary<br/>recap when available")]
R --> Q["Replay"]
Q --> E
The frontend's simulation timeline sees exactly what the event log sees; Pydantic event models in the backend mirror a TypeScript discriminated union in the frontend, guarded by a contract test. Recorded, live, headless, and visual runs are different views of the same simulation—not products that can drift.
Read the architecture guide for the prompt pipeline, wire contract, persistence model, cost accounting, and module-by-module tour.
Scaffold a package that already loads and lints:
township new-scenario school-boundary-vote
township new-agent school-boundary-vote townsville --name "Maya Brooks"
township run --scenario school-boundary-vote --provider mockscenarios/school-boundary-vote/
├── scenario.json # question, options, rounds, news, town order
├── towns/*.json # place, demographics, landmarks, map metadata
├── options/*.json # arguments, positions, evidence, sources
├── agents/<town>/*.md # fictional resident personas
├── context/*.json # logistics or other shared evidence
└── god-scenarios.json # optional intervention presets
The package can describe an election, a budget, a zoning hearing, a school-board decision, or another shared civic choice. The engine does not know the names of your towns or options. Start with the complete scenario format, the persona authoring guide, or copy the fully annotated persona template.
Set LLM_PROVIDER explicitly, or leave it unset and Township will detect a
configured credential before falling back—loudly—to mock.
| Provider | LLM_PROVIDER |
Credential / endpoint |
|---|---|---|
| Deterministic mock | mock |
None |
| AWS Bedrock | bedrock |
AWS_BEARER_TOKEN_BEDROCK or the standard AWS credential chain |
| Anthropic API | anthropic |
ANTHROPIC_API_KEY |
| OpenAI API | openai |
OPENAI_API_KEY |
| OpenRouter | openrouter |
OPENROUTER_API_KEY |
| Ollama | ollama |
Local OpenAI-compatible endpoint |
| LM Studio | lmstudio |
Local OpenAI-compatible endpoint |
Source installs and the Docker image include the OpenAI client; a plain wheel
install needs pip install 'township[openai]' for the OpenAI-compatible four.
Concurrency, model IDs, prompt caching, endpoints, CORS, and deployment options
are documented in Deployment and .env.example.
Secrets belong only in environment variables.
| Scenario | Cast | What ships |
|---|---|---|
| The Millbrook Surplus | 8 residents · 2 towns | A fictional direct-democracy vote over a one-time $12M surplus, plus a zero-cost mock replay. It proves the engine is not election-specific. |
| NJ-11 Special Election | 26 residents · 4 towns | A retrospective of the certified April 2026 race, with a complete Bedrock/Claude replay and a published error analysis. |
The included NJ-11 replay contains 883 events, 405 model calls, 1,501,405 metered token units, and zero failed agents. Its provider usage after generating the recap was $7.3298; the district summary, finalized just before that last recap call, records $7.3192.
The 26 fictional characters ended at 19 Mejia, 5 Hathaway, 0 Bond, and 2 undecided. Those are character states, not votes, percentages, a sample, or a forecast. The NJ-11 retrospective compares the run with certified results and documents selection bias, consensus drift, turnout blindness, and where the simulation failed. The exact event log is committed with the scenario, so the claims are reproducible.
| Path | Purpose |
|---|---|
backend/core/ |
Pydantic domain models, scenario/persona loading, wire DTOs, storage, EventBus |
backend/simulation/ |
Multi-round loop, multi-town orchestration, replay, recap, run persistence |
backend/providers/ |
Bedrock, Anthropic, OpenAI-compatible, local, and mock adapters |
backend/routes/ |
Simulation, chat, God's View, scenarios, towns, journal, runs, voice |
frontend/src/game/ |
Phaser world, resident sprites, clock, weather, routines, capture hooks |
frontend/src/components/ |
Map, town, chat, journal, dashboard, replay player, accessibility controls |
scenarios/<id>/ |
All domain-specific content and optional demo replay |
tests/ |
Offline backend, provider, persona, scenario, CLI, replay, and wire-contract tests |
- Documentation index — the shortest route to every guide
- Architecture · API · Deployment
- Scenario format · Persona authoring
- FAQ · Roadmap
- Responsible Use · Security
- Third-party art and font notices
Personas are the best first contribution: they need empathy and local texture, not Python. Scenarios add whole new civic worlds; engine, research, frontend, art, accessibility, and documentation work are equally welcome.
Read CONTRIBUTING.md, choose a
good first issue,
or propose a new resident
or scenario.
Community participation follows the Code of Conduct.
Township is released under the MIT License. Cite the software using
CITATION.cff; academic work building on its generative-agent
architecture should also cite Park et al., Generative Agents (UIST 2023), as
recorded in that file.
Vendored and derivative pixel assets remain subject to their asset-specific licenses and documented provenance qualifications. Every source, modification, and license is listed in THIRD_PARTY_NOTICES.md. Press and project media include the social preview and the brand kit.
Build a town worth listening to.














