Draw a distributed system. Press Run. Watch it break — before you ship it.
A React + Vite application for simulating, stress-testing, and analysing high-level system designs using Discrete Event Simulation (DES). It runs both as a browser SPA and inside an Electron desktop shell, powered by a G/G/c/K queueing engine under the hood.
You drag nodes onto a canvas (API servers, databases, caches, load balancers), connect them with edges, configure workload and runtime parameters, and then press Run. The engine simulates requests flowing through your architecture — tracking latency, queue depth, throughput, timeouts, rejections, and node utilization — without a real server in sight.
The current MVP surfaces run summaries, bottleneck checks, per-node metrics, and a canonical event replay log in the UI, and it also supports CLI and JSON-based output for scripted runs.
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Users │────────►│ Gateway │────────►│ API │
│ source │ https │ lb-l7 │ grpc │ micro │
│ 980 rps │ 1ms │ ██░░ 40% │ 0.5ms │ ████ 85% │
└──────────┘ └──────────┘ └─────┬────┘
│ tcp 2ms
┌──────▼────┐
│ DB │
│ postgres │
│ ██████ 97%│ ← bottleneck
└───────────┘
Drag nodes from the palette onto the canvas. Connect them. Configure each node's queue parameters (workers, capacity, service time distribution, timeout), resilience settings, edge behavior, and SLO targets. Adjust source selection, workload pattern, duration, warmup, timeout, and seed from the simulation controls before a run.
Press Run. The topology is serialized, validated, and executed inside a Web Worker. The discrete event loop processes events in time order, supports deterministic replay by seed, and exposes pause / resume / stop controls while the run is in progress.
When the simulation completes, a results tray expands with:
- Overview — latency percentiles, throughput, error rate, timeouts, and run context
- Bottlenecks — SLO breaches, Little's Law checks, conservation checks, and warmup adequacy
- Nodes — per-node throughput, queue depth, utilization, and latency
- Traffic — canonical replay preview plus a filterable event log
This project is structured into three major phases:
- Phase 1 · BUILD (Implemented)
- Phase 2 · SIMULATE (Implemented in the current MVP)
- Phase 3 · ANALYSE (Implemented in the current MVP, with additional views still planned)
It follows an iterative workflow:
Build → Simulate → Analyse → Iterate
Users visually construct and configure system topology.
flowchart TD
subgraph BUILD["Phase 1 · BUILD"]
B1["Drag LibraryItem onto canvas"]
B2["useFlowDnD → addNode() → Zustand store"]
B3["Draw edge → onConnect() → addEdge()"]
B4["Select node → PropertiesPanel reads store"]
B5["Edit config → updateNodeData() → store"]
B6["Save → JSON.stringify(nodes + edges) → FileService → browser API or Electron dialog"]
B7["Open → file picker or Electron dialog → FileService → setNodes() & setEdges()"]
B1 --> B2 --> B3 --> B4 --> B5
B5 --> B6
end
- Drag a
LibraryItemonto the canvas. useFlowDnDcallsaddNode()→ stored in Zustand.- Connect nodes →
addEdge()updates store. - Select a node →
PropertiesPanelreads state. - Edit config →
updateNodeData()updates state. - Save → serialized JSON → FileService → browser file API or Electron dialog.
- Open → browser file picker or Electron dialog → state restored.
A deterministic simulation engine runs inside a Web Worker.
flowchart TD
subgraph SIMULATE["Phase 2 · SIMULATE"]
S1["Press Run in the simulation controls"]
S2["Serialize Zustand store → topology + workload overrides"]
S3["Validate topology + derive run context"]
S4["postMessage → Renderer to Web Worker"]
S5["DES engine event loop + seeded PRNG + metrics collection"]
S6["Progress / snapshots / final output returned to UI"]
S1 --> S2 --> S3 --> S4 --> S5 --> S6
end
- Serialize topology, edges, and selected workload/global overrides.
- Validate the topology before execution.
- Send the resulting
TopologyJSONto a Web Worker. - Run the discrete event simulation (DES) engine.
- Emit progress updates and snapshots, then return the final output plus replay data.
Results are visualized and broken down for deeper insights.
flowchart TD
subgraph ANALYSE["Phase 3 · ANALYSE"]
A1["Results Tray expands"]
A2["Overview → latency · throughput · error rate · run context"]
A3["Bottlenecks → SLOs · Little's Law · conservation · warmup checks"]
A4["Nodes → utilisation · queue depth · per-node latency and throughput"]
A5["Traffic → canonical replay preview + filterable event log"]
A1 --> A2 & A3 & A4 & A5
end
- Latency percentiles (P50–P99)
- Throughput & availability
- Per-node utilisation metrics
- Replayable canonical event stream
- SLO breach detection
- Warmup, conservation, and Little's Law checks
flowchart LR
BUILD["BUILD"] -->|"Run"| SIMULATE["SIMULATE"]
SIMULATE --> ANALYSE["ANALYSE"]
ANALYSE -.->|"Iterate · Adjust · Re-run"| BUILD
| Layer | Technology |
|---|---|
| App shell | Browser SPA + Electron 38 desktop shell |
| Build system | Vite 7 + electron-vite 4 |
| UI framework | React 19 + TypeScript 5 |
| Styling | Tailwind CSS 3 |
| Canvas | React Flow 11 |
| State management | Zustand 5 |
| Validation | Zod 4 |
| Testing | Vitest 4 |
| Simulation engine | In-repo DES engine + Web Worker execution |
| Node | Type | Description |
|---|---|---|
| API Server | computeNode |
Long-running process, configurable CPU/queue |
| Serverless Fn | computeNode |
Event-driven, low baseline utilization |
| Job Worker | computeNode |
Background task processing |
| Cron Job | computeNode |
Scheduled execution |
| Primary DB | serviceNode |
Relational SQL datastore |
| Redis Cache | serviceNode |
In-memory key/value store |
| Load Balancer | serviceNode |
L7 request routing |
| VPC Region | vpcNode |
Isolated network boundary / grouping |
The engine is a Discrete Event Simulation loop — no real clocks, no real servers, only a priority queue of timestamped events processed in order.
Each node is modelled as a G/G/c/K queue:
c— concurrent workersK— max queue capacity (excess arrivals are rejected)- Service time sampled from a configurable probability distribution (log-normal, exponential, Poisson, Weibull, etc.)
Core runtime pieces currently in the repo:
| Component | Role |
|---|---|
| Min-Heap | O(log n) event priority queue |
| BigInt time utilities | Deterministic microsecond scheduling |
| Seeded PRNG | Same seed = identical results |
| G/G/c/K Node | Per-node queue model with workers and capacity |
| Workload Generator | Constant / Poisson / Spike / Diurnal / Bursty / Sawtooth traffic |
| Routing Table | Weighted path resolution and fan-out handling |
| Topology Validator | Zod-backed schema + structural validation |
| Metrics Collector | Latency, throughput, error rate, SLO, and queueing checks |
| Request Tracer | Per-request span collection |
| Canonical Event Stream | Replay-friendly lifecycle/event recording |
| Web Worker Runner | Off-main-thread simulation execution |
Advanced fault modeling, broader resilience semantics, and richer post-run analysis are also documented in ns-simulator-docs/ and are being expanded incrementally in the runtime.
The ns-simulator-docs/ directory is a Git submodule containing the theory, specs, planning material, generated reference artifacts, and workflow assets that support the simulator:
ns-simulator-docs/
├── docs/
│ ├── SYSTEM_OVERVIEW.md # End-to-end system reference
│ ├── theoretical-foundations.md # Queueing theory, DEVS, reliability
│ └── 01-05-*.md # Core documentation series
├── schema/ # Canonical TypeScript schema + schema docs
├── canonical-catalogue/ # 17 CSV reference files + catalogue README
├── planning/
│ ├── IMPLEMENTATION_PLAN.md # 10-phase build plan
│ ├── TICKETS.md # 46 engineering tickets
│ └── analysis/ # Planning / meeting analysis notes
├── design-decisions/ # ADRs and issue writeups
├── specs/ # Feature and semantics specifications
├── generated/ # Generated topologies and sheet exports
├── skills/ # Codex skills for docs/schema/scenario workflows
├── tools/ # Helper scripts
├── stitch_simulation_output_analysis/ # UI reference captures
└── system-mind-map.md # High-level concept map
Notable additions in the current submodule checkout are skills/, generated/, specs/, tools/, and the expanded design-decisions/ set.
To initialise the submodule after cloning:
git submodule update --init --recursive- Node.js 20+
- npm
npm installnpm run dev:web # browser SPA
npm run dev:electron # Electron desktop shellnpm run typechecknpm run build:web # browser production bundle
npm run build:electron # Electron production bundle
npm run build:all # both targets, same path used in CI
# Electron installers
npm run build:electron:mac
npm run build:electron:win
npm run build:electron:linuxnpm run testnpm run simulate -- order-topology.json
npm run simulate -- order-topology.json --json- Deterministic by default — every simulation run is seeded; the same seed always produces identical output
- No decorative animation — every visual (colour change, edge pulse, queue bar) represents real simulation data
- Mathematical transparency — metrics show their formula on hover (e.g.
utilization = activeWorkers / maxWorkers) - Desktop-first — minimum 1280px viewport; no mobile layout compromise
- Single source of truth — canvas, inspector panel, and JSON topology viewer all read from and write to one Zustand store
To keep demos and analysis trustworthy, parameters are classified into four classes:
- Invariant (hardcoded): simulator mechanics and safety guards that should not vary per scenario.
- Default + Override: defaults are provided, but users can override them per node/edge.
- User Parameter: visible controls that must affect simulation behavior.
- Not Simulated: fields retained for UX/modeling completeness that do not currently affect runtime behavior.
- Event ordering and tie-breaking (timestamp -> priority -> stable sequence)
- Core queue semantics (
fifo/lifo/priority/wfq) - Time-unit internals (microsecond scheduling)
- Safety clamps and bounds
Edges now use serializer defaults only as fallback and can be configured per edge:
- Protocol and mode
- Latency distribution (
mu,sigma) andpathType - Bandwidth
- Max concurrent requests
- Packet loss rate
- Edge error rate
Visible node controls that currently influence runtime behavior:
- Queueing knobs (
workers,capacity,queueDiscipline,meanServiceMs,timeoutMs) - Throughput/load/queue hints
vCPUandRAM(deterministic mapping to derived queue/perf behavior)status(healthy/degraded/critical performance/error impact)- Service
errorRate(node-level failure injection) - Security
blockRateanddroppedPackets(arrival-time rejection/timeout behavior)
Fields that are not wired to runtime behavior are hidden from the default inspector UI.
| Area | Status |
|---|---|
| Browser SPA target | Done |
| Electron desktop target | Done |
| React Flow canvas (nodes + edges) | Done |
| Drag-and-drop node palette | Done |
| Node types (Compute, Service, VPC) | Done |
| Atomic design system (atoms → organisms) | Done |
| Zustand topology store | Done |
| Inspector panel | Done |
| File save / load via browser APIs and Electron dialogs | Done |
| Scenario controls (source/workload/global runtime) | Done |
| Topology serialization + validation | Done |
| Simulation engine (DES loop) | Done |
| Web Worker execution | Done |
| Results tray (overview / bottlenecks / nodes / traffic) | Done |
| CLI simulation runner | Done |
| Advanced fault authoring UI | Partial |
| Advanced cost / failure analysis UI | Planned |
See ns-simulator-docs/planning/TICKETS.md for the full 46-ticket breakdown.
git clone <repo-url>
cd ns-simulator
git submodule update --init --recursive # pulls ns-simulator-docs
npm install
npm run dev:webfeature/<kebab-case-description> # new functionality
fix/<kebab-case-description> # bug fixes
Examples from this repo: feature/add-ui-dependencies, feature/implement-vpc, feature/react-flow-canvas-setup.
Imperative mood, sentence case, no period.
Add Tailwind CSS, ReactFlow, Zustand, and UI dependencies
Fix circuit breaker state not resetting on node recovery
Refactor VPC node to extract header and toolbar molecules
npm run typecheck # tsc across both node + web tsconfigs
npm run lint # eslint
npm run test # vitest
npm run build:all # browser + Electron production builds
npm run format # prettier --writeThe branch should stay green on both targets. build:web, build:electron, and build:all all run typecheck automatically.
- One concern per PR — don't bundle UI changes with engine work.
- Title: short, imperative, under 70 characters.
- PRs targeting the simulation engine require a note on determinism: confirm the change does not break seed reproducibility.