---
title: "Civitas"
url: "https://aigamehub.app/zh-tw/games/civitas-f09314"
source: "https://github.com/reneodlt/civitas"
source_type: "github"
play_url: "https://civitas.reneo.app"
platform: "desktop"
login_required: false
pricing: "free"
origin: "original"
ai_tools: ["Claude Code"]
game_languages: ["en"]
author: "reneodlt"
author_handle: "reneodlt"
category: "模擬"
categories: ["simulation"]
tags: ["city-builder", "multiplayer", "open-source", "three.js", "typescript"]
added: "2026-10-19T13:22:30.741Z"
updated: "2026-10-19T13:22:30.741Z"
locale: "zh-tw"
content_locale: "zh-tw"
---

# Civitas

Civitas 是一款以瀏覽器為基礎的多人城市建造遊戲，客戶端使用 three.js，並透過 WebSockets 連接一個權威式 TypeScript 模擬伺服器。玩家購買與管理土地、劃分土地用途、建造道路並設定稅率；市民會個別模擬，住在他們生成的地方，在相連的道路網路上尋找最近的工作，每天通勤，並向他們工作所在地的地主繳納所得稅，向他們居住所在地的地主繳納財產稅。它可在瀏覽器中執行，也可透過存儲庫自行託管。

## Description

Civitas（在 README 中也稱為 msimcity）是一款基於網頁、多人、無邊界的城市建造遊戲。客戶端使用 three.js；一個權威式 TypeScript 模擬伺服器透過 WebSockets 通訊，並有一個共享套件定義雙方的世界格式。玩家購買與管理土地，但市民並非由玩家擁有：市民住在生成它的住宅建築中，在相連的道路網路上尋找任何地方最近的工作，每天通勤到那裡，並向它工作所在地的地主繳納所得稅，向它居住所在地的地主繳納財產稅。競爭與合作是透過土地劃分、道路與稅率來進行，而非透過圍牆。多個瀏覽器加入同一個伺服器會共享同一個世界。建築物被定義為資料檔案，並編譯成遊戲讀取的表格，而一個檢視器頁面會顯示每一種、每一等級與每一設計。本專案採用 AGPL-3.0 授權，可使用 npm install 與 npm run dev 在本機執行，或在生產環境使用 npm run build 與 npm start；其中包含一個 Dockerfile，並且可透過掛載 /data 來持久化世界。環境變數涵蓋 port、host、seed、world file 與 static directory，並提供一個唯讀的展示模式（SPECTATE=1）。作者表示本專案是使用 Claude Code 建置的。

## Original post

Civitas: a browser-based multiplayer city builder in three.js and TypeScript where every citizen is simulated. Built with Claude Code. AGPL-3.0.

Topics: city-builder, claude-code, game, multiplayer, simulation, threejs, typescript

# msimcity A web-based, multiplayer, unbounded city builder platform. three.js on the client, an authoritative TypeScript simulation server over WebSockets, and a shared package so both sides agree on the world format. The core idea: players buy and manage **land parcels**, but **citizens are not theirs**. A citizen lives in whichever residential building spawned them, looks for the nearest job anywhere on the connected road network, commutes there every day, pays income tax to whoever owns the parcel they work in and property tax to whoever owns the parcel they live in. Players compete and cooperate through zoning, roads and tax rates, not through walls. ## Run it ```sh npm install npm run dev # server on :8080 and Vite client on :5173, both bound to 0.0.0.0 ``` Open `http://<your-lan-ip>:5173/` (the Vite banner lists the addresses), pick a name, and you are in. Every browser that joins shares the same world. Buildings are data: `packages/shared/src/buildings/` holds one file per kind family, and `npm run buildings:compile` (run automatically by dev and build) turns it into the tables the game reads. `/viewer.html` shows every kind, level and design on a turntable by day and night. Format and a worked example: `docs/buildings.md`. `npm run buildings:check` is its snapshot test. Production: `npm run build` then `npm start`. The server then serves the built client and the WebSocket on one port (default 8080). A `Dockerfile` is included; mount `/data` to keep the world across restarts. Environment: `PORT`, `HOST`, `SEED` (new worlds only), `WORLD_FILE`, `STATIC_DIR`. Dev knobs: `SIM_STEPS=10` fast-forwards (ten simulation ticks per interval), `SEED_BUILT=3` starts a new world with the Commons already built to that level, and a file named `RESET` next to the world file makes the next start discard it. Client: `VITE_WS_URL` to point at a server on another host, or `?ws=host:port`. `TEST_CITY_BASE_PORT` (default 8100) moves the test-city ports. A public showcase (`docs/demo.md`, "A public showcase"): `SPECTATE=1` makes the whole deployment read-only for every client, the live world and every demo child alike (the children inherit it). The server refuses every intent that changes the world with a "Read-only showcase" toast (a whitelist in `net.ts`, `READ_ONLY`: subscriptions, inspects, the lines list, the charts), and the HTTP API refuses demo stop and reset and test-city start and stop (403); starting a demo still works, as that is how a visitor gets in. The page hides the tool bar, the treasury, the tech tree, Reset demo and the context menu's changes, and joins at once as a made-up "Visitor 1234" instead of showing the join form. `DEFAULT_DEMO=<id>` (e.g. `london`) sends a page opened without `?demo=`, `?ws=` or `?city=` to that demo instead of the live world, and the server starts it as it starts; with `SPECTATE` it is then the only demo the server lists, starts or joins (a `?demo=<other>` link opens it instead). The page reads both from `GET /api/
