Particle Swarm Simulator
Generate massive particle swarm simulations with a clean separation of concerns: a host runtime handles Three.js rendering, camera, input, gesture recognition, and security — the AI writes only a behavior function body that positions and colors 20,000+ particles per frame.
Not a narrative canvas. Not a phase engine. A sandbox where math becomes motion.
Quick Start
# Generate a sandbox runtime with gesture controls
/particle-swarm-sim --mode sandbox dark theme, 30K particles, gesture OS enabled
# Generate a sim function body
/particle-swarm-sim --mode sim aurora borealis — curtains of light flowing along magnetic field lines
# Generate from preset
python3 scripts/swarm_generator.py --mode sim --preset galaxy-spiral --output galaxy.js
The Separation of Concerns
HOST RUNTIME BEHAVIOR FUNCTION BODY
(sandbox-runtime.html) (generated by AI)
Three.js scene target.set(x, y, z)
BufferGeometry (20K particles) --> color.setHSL(h, s, l)
Camera + input + gesture OS addControl("speed", ...)
Security validator setInfo("title", ...)
Code injection pipeline annotate("label", ...)
Dynamic controls UI
HUD + annotations
The host calls the function body 20,000 times per frame — once per particle. The function body mutates the provided target and color objects. Nothing else crosses the boundary.
API Contract
Read-Only Variables
| Variable | Type | Description |
|---|---|---|
i | Integer | Particle index (0 to count-1) |
count | Integer | Total particle count |
time | Float | Global simulation time in seconds |
THREE | Object | Safe Three.js subset: Vector3, Color, MathUtils |
Write-Only Variables
| Variable | Type | Mutation |
|---|---|---|
target | THREE.Vector3 | target.set(x, y, z) — particle position |
color | THREE.Color | color.setHSL(h, s, l) or color.set(hex) — particle color |
Helper Functions
| Function | Signature | Notes |
|---|---|---|
addControl | (id, label, min, max, initial) => float | Creates UI slider, returns current value |
setInfo | (title, description) | Updates HUD. Guard: if (i === 0) |
annotate | (id, x, y, z, labelText) | Floating 3D label. Guard: if (i === 0) |
For setInfo and annotate: call only when i === 0. Calling per-particle creates 20,000 DOM updates per frame. The annotate function takes numeric coordinates (not a Vector3) to avoid per-frame allocations.
Full specification: references/api-contract.md
Mode: sandbox
Generate the complete host runtime HTML — the sandbox that executes behavior function bodies.
Configuration
| Parameter | Default | Range | Effect |
|---|---|---|---|
particleCount | 20000 | 5000-50000 | Total particles in the swarm |
particleSize | 0.06 | 0.01-0.5 | Point size (screen-space) |
backgroundColor | #050508 | hex | Scene background (never pure black) |
autoOrbitSpeed | 0.1 | 0-1.0 | Camera auto-rotation rad/sec |
cameraRadius | 30 | 5-100 | Default camera distance |
gestureEnabled | true | bool | MediaPipe Hands activation |
injectionEnabled | true | bool | Code editor textarea visible |
Architecture
init() --> createScene() --> createParticles(count) --> setupCamera() --> setupInput()
|
setupGestureOS() --> setupInjection() --> setupHUD()
|
animate() <-- requestAnimationFrame <-- executeSimFunction() --> updateBuffers()
|
Per frame: for (i = 0; i < count; i++) { simFn(i, count, time, THREE, target, color, ...) }
|
geometry.attributes.position.needsUpdate = true
geometry.attributes.color.needsUpdate = true
Templates
| Template | Lines | Includes |
|---|---|---|
assets/templates/sandbox-runtime.html | ~900 | Full: gesture OS, code editor, security validator, HUD, annotations |
assets/templates/sandbox-minimal.html | ~400 | Stripped: mouse/touch only, no code editor, no gesture — for embedding |
Full architecture: references/sandbox-architecture.md
Mode: sim
Generate particle behavior function bodies from creative concepts. Output is a JS function body — no HTML, no Three.js setup. The body gets injected into a sandbox runtime.
Concept-to-Form
| Concept Domain | Position Strategy | Color Strategy | Controls |
|---|---|---|---|
| Orbital / astronomical | Spherical coords + angular velocity | HSL hue from angle | orbit radius, speed |
| Biological / organic | Parametric curves + noise perturbation | Warm gradients from position | growth rate, density |
| Geometric / crystalline | Lattice positions + modular arithmetic | Discrete palette from i % N | symmetry order, scale |
| Fluid / flow | Curl noise or velocity field | Hue from velocity magnitude | viscosity, turbulence |
| Musical / rhythmic | Sin/cos with frequency ratios | HSL from beat phase | tempo, harmonics |
Sim Presets
Galaxy Spiral — Logarithmic arms with density wave theory. i % arms grouping. HSL from arm index + distance falloff. Controls: arm count (2-8), spin rate, thickness. Reference: assets/sims/galaxy-spiral.js
DNA Helix — Double helix with base pair rungs. i % 3 splits into strand A, strand B, rungs. Controls: helix radius, pitch, rung density. Reference: assets/sims/dna-helix.js
Murmuration — Boids-like flocking approximated via phase-shifted oscillators. No neighbor search, no arrays — purely mathematical. Controls: flock radius, speed, coherence. Reference: assets/sims/flocking-murmuration.js
Torus Knot Flow — Particles trace a (p,q) torus knot with index-based phase offset. HSL from progress along the knot. Controls: p, q, flow speed.
Lorenz Attractor — Time-stepped Lorenz system positions, particle index as time offset. Color from z-height. Controls: sigma, rho, beta.
Reaction-Diffusion Shell — Cone-shaped shell with Turing-like stripe patterns computed analytically. Controls: wavelength, cone aperture.
Full gallery with math breakdowns: references/sim-gallery.md
Minimal Valid Sim
const speed = addControl('speed', 'Speed', 0.1, 5.0, 1.0);
if (i === 0) setInfo('Expanding Sphere', 'Uniform distribution on sphere surface');
const phi = (i / count) * Math.PI * 2 * 10;
const theta = Math.acos(1 - 2 * (i / count));
const r = 10 + Math.sin(time * speed + i * 0.01) * 2;
target.set(
r * Math.sin(theta) * Math.cos(phi),
r * Math.sin(theta) * Math.sin(phi),
r * Math.cos(theta)
);
color.setHSL(i / count, 0.8, 0.5 + 0.2 * Math.sin(time + i * 0.1));
Performance Rules
This function runs 20,000 times per frame at 60fps = 1,200,000 calls per second.
| Rule | Why | Wrong | Right |
|---|---|---|---|
| Zero allocations | 1.2M objects/sec overwhelms GC | new THREE.Vector3(x,y,z) | target.set(x,y,z) |
| Math over logic | Branch prediction fails at scale | if (i < count/2) {...} else {...} | Math.sin(i / count * Math.PI) |
| Mutate, never replace | Target/color are shared references | target = new THREE.Vector3(...) | target.set(x, y, z) |
| All values finite | NaN poisons the entire buffer | 1 / x where x could be 0 | 1 / (x + 0.0001) |
| Declare all variables | Strict mode violations | x = 5; | const x = 5; |
| No string operations | Concatenation creates GC pressure | "particle_" + i | Numeric IDs only |
| Constants outside loops | addControl creates DOM on first call | Call addControl inside conditionals | Call at function body top |
Full performance patterns: `reference