Murmur: ECS-Based Emergent Simulation
Overview
Murmur explores how modern systems achieve performance and scale through data-oriented design. Rather than building agents as individual objects, Murmur organizes thousands of entities and their behaviors into a data-first architecture inspired by Entity-Component-System (ECS) patterns.
The project demonstrates how runtime performance emerges from architectural choices, how to measure and reason about bottlenecks in real-time systems, and how the same design patterns apply across domains, from game engines to web simulations to distributed platforms.
Why This Matters
Most simulation systems begin with object-oriented thinking: "I'll create an Agent class, give each agent behavior, and instantiate 5000 of them."
This approach works until performance becomes the constraint. At 5000 agents, the overhead of object-oriented design becomes visible: cache misses, pointer chasing, method dispatch overhead, all multiplied across thousands of instances.
ECS inverts this approach: organize data first, then write systems that operate on that data efficiently.
The target is a system capable of simulating 5000 agents at 60 FPS in a browser, while remaining deterministic, debuggable, and observable.
Murmur explores these principles by building a flocking simulation from scratch. No physics engine, no agent library. Instead: raw ECS, custom spatial hashing, and direct measurement of where CPU cycles go.
What I'll Learn
By building Murmur, I'll better understand:
Data-Oriented Design:
- Why data layout matters for performance (cache efficiency, memory locality)
- How to reorganize code around data rather than objects
- When and why ECS patterns apply
Real-Time System Performance:
- How to identify bottlenecks (physics vs render)
- How architectural choices directly impact frame rate
- How to measure and reason about performance constraints
Spatial Algorithms:
- How spatial hashing works and why it's essential
- When to use spatial partitioning vs brute force
- How to tune data structures for specific problems
Web Graphics and Rendering:
- How instanced rendering works (one draw call for many entities)
- How to efficiently update geometry data each frame
- How GPU and CPU bottlenecks differ
ECS Across Contexts:
- How ECS patterns exist in game engines, web systems, and beyond
- How the same design principles scale across domains
- How to implement ECS without relying on a specific framework
Core Idea
Rather than agents being objects, they are pure data:
Then systems operate on these data arrays:
Each system processes all entities of that type in a tight loop. This is cache-friendly and scales linearly with agent count.
Architecture Walkthrough
The System Schedule
Every frame follows a deterministic order. This ensures correct behavior and reproducibility:
Why this order? Movement must happen before the next frame's spatial hash rebuild. The hash grid describes positions after movement, so steering the next frame is based on current positions.
How Data Flows
Step 1: Spatial Hashing
The world is divided into a grid. Each cell is a bucket:
For each agent, you calculate which cell it occupies:
This is O(n). You visit each agent once and place it in a cell.
Step 2: Steering Queries
When Agent 42 needs to find neighbors, instead of checking all 5000 agents:
The spatial hash reduces neighbor checks from a complexity of O(n²) to O(n). The exact number of comparisons per agent depends on grid resolution and cell occupancy.
Step 3: Steering Calculation
For each agent, calculate three forces:
- Separation: Move away from neighbors that are too close
- Alignment: Match the average velocity of neighbors
- Cohesion: Move toward the center of the neighbor group
- Attraction: Move toward the mouse cursor (user-controlled)
Apply these forces to velocity, clamp to maxSpeed, store the result.
Step 4: Movement
Apply velocity to position
All contiguous. Cache-friendly. Fast.
Step 5: Rendering and Measurement
Send positions to GPU via instanced rendering and measure performance:
What Performance Teaches
By exposing physics time, render time, and FPS, users see exactly where bottlenecks appear:
- Add 1000 agents: Physics time increases slightly, FPS drops slightly
- Add 5000 agents: Physics time dominates, render time stays flat (GPU is efficient)
- Change grid size: Watch spatial hash efficiency change as cell sizes vary
This is observable system behavior. Users see cause and effect.
Tech Stack
JavaScript and TypeScript (Runtime)
JavaScript is the browser's native language. You can build real-time, data-intensive systems in modern JS. Performance-critical code runs in tight loops where JavaScript's JIT compiler makes it competitive with compiled languages for this workload.
More importantly, this demonstrates you can build high-performance systems across different technology stacks, not just game engines, but web platforms.
Trade-off: JavaScript is slower than compiled languages (C++, Rust), but proper data structures (contiguous arrays, spatial hashing) matter more than language choice for this scale.
three.js (Rendering)
three.js is the industry standard WebGL wrapper. It abstracts away verbose WebGL boilerplate while exposing the features you need: geometry, instanced rendering, camera control, scene management.
It integrates seamlessly with JavaScript and handles the graphics pipeline cleanly.
Alternative considered: Raw WebGL would give you lower-level control but requires 10x more code for no benefit here. Babylon.js would be equally valid.
Trade-off: Abstraction over raw graphics APIs, but you retain control over rendering strategy (instanced draws, buffer updates).
ECS Architecture (System Design)
I have worked with Unity's ECS framework in game development. But understanding ECS fundamentally, not just as a framework, requires implementing it from scratch in a different context.
By building ECS in a web environment without an engine, you see:
- Why data layout matters (cache efficiency)
- Why system ordering is critical (determinism)
- How to measure performance impact of architectural choices
This understanding transfers to any system: game engines, distributed platforms, simulations, etc.
What ECS provides:
- Entities (pure data, indexed by ID)
- Components (data arrays: positions, velocities, behaviors)
- Systems (logic that operates on component data)
- Deterministic scheduling (systems run in a fixed order)
Spatial Hashing (Data Structure)
Spatial hashing is a fundamental data structure for any system dealing with proximity queries: simulations, graphics, physics, game engines, robotics.
Understanding it deeply, not as a black box library, means you can:
- Recognize when neighbor queries are your bottleneck
- Tune grid resolution for your specific case
- Adapt the pattern to different problems (2D flocking, 3D particles, large-scale simulations)
For Murmur specifically, spatial hashing reduces neighbor queries from O(n²) to O(n), making 5000 agents feasible.
Implementation: A simple grid (2D array) and hash function to map positions to cells. Roughly 100 lines of code.
performance.now() API (Profiling)
Real-time systems are only real-time if you measure them. By instrumenting the code with performance.now() calls, you make bottlenecks visible.
Users can see: "Adding 1000 agents added 2ms to physics time" or "Render time is capped by GPU bandwidth."
This is a teaching tool as much as a diagnostic tool.
What you measure:
- Physics time (spatial hashing plus steering plus movement)
- Render time (GPU operations)
- Frame time (total)
- FPS (derived from frame time)
Scalability and Interactivity
Murmur includes real-time control:
- Agent count slider: Add or remove agents instantly, watch performance metrics update
- Mouse as attractor: Click and drag to steer the flock in real-time
- Performance metrics display: See physics time, render time, and FPS live
This makes the relationship between architecture and performance tangible and immediate.
Status
Designed.
Architecture, data flow, and tech stack are documented and understood. Implementation can begin with:
- ECS core structure (entities, components, systems)
- Spatial hashing grid implementation
- Steering behavior system (separation, alignment, cohesion)
- three.js rendering and instanced draw
- Performance measurement and metrics display
- Mouse attractor interaction
What This Sets Up
Murmur establishes the execution plane. How to build high-performance runtime behavior in web systems. Once complete, it becomes the simulation engine that Zephyr controls through Nimbus's governance layer, demonstrating how architecture decisions directly reshape observable behavior.