A browser-based distributed-systems simulator with chaos engineering. Build topologies on a canvas, watch traffic flow tick by tick, then break things and see how the metrics react.
Vanilla TypeScript + Vite + a single <canvas>. No UI framework, no backend simulation — the entire model runs in the browser. Production is a static bundle behind a tiny Express server.

npm install
npm run dev # http://localhost:5173Press S to start the simulation, click an empty space and press N to drop a node, drag from a node's right-edge port to another node's left-edge port to connect them. Or pick a preset from the toolbar to skip the setup.
For the full keyboard / mouse reference, see docs/controls.md.
DistroSim lets you:
- Build a topology with 18 node types: clients, load balancers, API servers, databases, replicas, caches, queues, CDNs, key-value stores, object stores, message brokers, search indexes, DNS, service mesh, rate limiters, auth services, WAFs, and config stores.
- Run a 4 Hz tick simulation that sources traffic from clients, propagates it downstream, models per-node capacity and latency, and reports global metrics (availability, p99 latency, throughput, error rate).
- Break things with 63 bespoke chaos events organized into Infrastructure, Network, Application-level, and Data-layer categories — from instance crashes and AZ failures to cache poisoning, split-brain, GC pauses, LSM compaction storms, and TLS certificate expiry.
- Watch the impact in real time via animated packets, per-node load halos, an event log, and 60-second sparklines.
- Save and replay interesting topologies as JSON.
The simulation is opinionated and approximate — it uses an M/M/1-style queueing approximation for service latency and a simple BFS-from-clients ordering for traffic propagation. It's a tool for intuition, not capacity planning. See docs/simulation.md for the full model.
Detailed documentation lives in docs/. Start here:
| Document | What's in it |
|---|---|
| docs/architecture.md | Stack, repo layout, module dependency graph, data flow per frame |
| docs/simulation.md | What runTick() does — traffic sourcing, ordering, latency, p99 |
| docs/node-types.md | All 8 node types: defaults, behavior, capacity, status transitions |
| docs/chaos.md | Kill, partition, latency, cascade — and how to add new primitives |
| docs/presets.md | Built-in topologies, what they're useful for, how to add your own |
| docs/controls.md | Full keyboard, mouse, toolbar, and sidebar reference |
| docs/metrics.md | Global metrics, sparklines, severity thresholds, per-node fields |
| docs/deployment.md | Docker, Railway, Render, Fly.io, Cloud Run, health checks, env vars |
| docs/development.md | Local dev workflow, typecheck, conventions, where to add features |
DistroSim/
├── README.md # ← you are here
├── docs/ # Detailed documentation (linked above)
├── index.html # Single-page entry; loads /src/main.ts
├── src/
│ ├── main.ts # Top-level controller: input + render loop
│ ├── canvas.ts # Canvas rendering
│ ├── simulation.ts # Per-tick traffic + latency + error model
│ ├── chaos.ts # Kill, partition, latency, cascade
│ ├── nodes.ts # Node creation, hit-testing, ports
│ ├── edges.ts # Edge geometry (bezier) + hit-testing
│ ├── presets.ts # Built-in topologies
│ ├── metrics.ts # Sparkline rendering + formatters
│ └── types.ts # Shared types and constants
├── server.cts # Express static server (TS source)
├── server.cjs # Compiled server (build output)
├── Dockerfile # Multi-stage Node 20 Alpine
├── Makefile # `make help` for dev/build/serve/docker
├── railway.toml # Railway deploy config
├── vite.config.ts
├── tsconfig.json # client TS config
└── tsconfig.server.json # server TS config
A walk-through of every module and how they depend on each other lives in docs/architecture.md.
npm run dev # Vite dev server with HMR (port 5173)
npm run build # vite build + tsc -p tsconfig.server.json
npm start # node server.cjs (port 3000 or $PORT)
npm run preview # Vite preview of the production buildThere's also a Makefile with background-server, Docker, and cleanup targets:
make help # list every target
make dev-bg # vite in the background
make serve-bg # production server in the background
make docker-build # build the Docker image
make typecheck # tsc --noEmit
make clean # remove dist/ server.cjs .pids/Full reference in docs/development.md.
DistroSim is stateless. Anything that runs node server.cjs and forwards $PORT works.
# Local
npm run build && PORT=3000 npm start
# Docker
docker build -t distrosim .
docker run -p 3000:3000 distrosimrailway.toml is pre-configured for Dockerfile builds and /healthz health probes. Render, Fly.io, and Cloud Run all work out of the box.
Full setup notes for each platform: docs/deployment.md.
The codebase is small and approachable: ~10 TypeScript modules, no framework, one mutable state object. Common entry points:
- Adding a node type → docs/node-types.md + docs/simulation.md
- Adding a chaos primitive → docs/chaos.md
- Adding a preset → docs/presets.md
- Tuning the feel → docs/development.md
Strict TypeScript everywhere; see docs/development.md for conventions.