Skip to content

Repository files navigation

⚗️ Chemical Chaos Engine

Vanilla JS Zero Dependencies Architecture Version License

A real-time, high-performance 2D molecular dynamics and chemistry simulation engine — built in pure JavaScript, running at 60 FPS in any browser.
No WebGL. No frameworks. No runtime dependencies. Just physics, stochastic chemistry, and raw CPU performance.

🔬 Live Demo — bourier.biz


🎯 Design Philosophy

This is a heuristic visualization of chemistry, not a quantum-mechanically accurate simulator.

The Chemical Chaos Engine uses stochastic models and normalized pseudo-units to create emergent, visually compelling molecular behavior. Reactions are not driven by activation energy thresholds or DFT calculations — they fire with tunable probabilities based on proximity, temperature, and local electronegativity. Think of it as a molecular sandbox: the rules are chemically inspired, not chemically derived.

Known boundaries of the model:

  • Not quantum-mechanically accurate (classical approximation)
  • Uses normalized pseudo-units, not SI
  • Temperature is a kinetic energy proxy, not a full thermodynamic state variable
  • Reactions are stochastic (probability-gated), not activation-energy-based
  • Physics runs on the main JavaScript thread — Web Worker / OffscreenCanvas migration is a future goal

🤯 Generic Physics Engines vs. Chemical Chaos Engine

The typical web physics approach: You reach for Matter.js or Box2D. These model circles with mass, not atoms with chemistry. Valency, electronegativity, covalent bond order, formal charges, aromaticity — all absent. Grafting organic chemistry on top of a generic engine means fighting the library at every step.

The Chemical Chaos Engine approach (Domain-First): The physics engine was designed around chemistry, not the other way around. Every particle is a real atom — element, valence electrons, formal charge, reactivity. The physics loop is a reaction solver that runs VSEPR geometry, bond order constraints, and 30 organic reaction pathways at every frame. Spawn Carbon, Hydrogen, and Oxygen at high temperature and watch them spontaneously assemble into alcohols, aldehydes, and carboxylic acids — without scripting a single reaction manually.


✨ Core Features

Feature Details
⚛️ Atom-Inspired Chemistry Model Valency enforcement (C=4, N=3, O=2, H=1), dynamic hypervalency (S, P, N), formal charges via FC = V − N − B/2
🔗 Bond Order System Single, double, and triple bonds with per-order Lennard-Jones repulsion and geometry constraints
🔥 Organic Reactions ~30 reactions: SN1/SN2, Aldol, Diels-Alder, Esterification, Keto-Enol, Cope [3,3], free-radical polymerization, Wittig, Michael addition, and more
🧪 Ionic & Acid-Base Chemistry Full pH tracking from live H₃O⁺/OH⁻ counts, neutralization, generic proton donation, HCl strong-acid dissociation, water auto-ionization
💡 Aromatic Systems Hückel rule detection, π-conjugation propagation, EAS and SNAr reactions, aromatic turbulence immunity
🌡️ Thermodynamics Stochastic reaction rates and particle velocities scale with global temperature (0–400 K); local thermal zones create spatial gradients
🗺️ Spatial Hash Grid 4096-bucket bitmask hash; O(N) collision detection; pooled Int32Array neighbor cache — zero GC, frame-counter invalidation
🧮 Semi-Implicit Euler Symplectic integration for long-term energy conservation
🔗 Disjoint Set Union Near-constant O(α(N)) molecular identity tracking for ring and structure detection
🎯 Temporal Amortization Heavy workloads (pH, organic reactions, structure detection) frame-sliced across a 60-frame cycle to hold render budget
🔍 "HPC" In-Engine Profiler Per-frame kernel timing (CPU/ALU/BW/RAM/GPU tags) with 150-frame rolling averages
🎆 Dynamic Visuals sp³/sp²/sp orbital clouds, R/S chirality labels, formal charge overlays, electronegativity bond gradients, dynamic pH background
🏆 Achievement System 50+ unlockable chemistry milestones (molecule detections, reaction counts, physics events)
🧬 1707 Molecule Database Hill-notation keyed registry for real-time structural recognition

📊 Real-Time Benchmarks (v32.0)

Measured on Chrome V8, Windows 11, 1920×1080 — physics on a single CPU thread.

Scenario Particle Count Active Bonds Physics Latency Render Latency GC Pauses FPS
Idle 0 0 < 0.1 ms ~0.5 ms 0 60
Light 200 150 ~0.5 ms ~2.0 ms 0 60
Medium 600 500 ~1.8 ms ~4.5 ms 0 60
Heavy 1200 1100 ~4.2 ms ~8.0 ms 0 60
Stress 2500 2300 ~11.0 ms ~14.0 ms Minimal 35-45

** Kernel Telemetry (1048 particles, desktop, uncapped):**

  • Total physics latency: ~5 ms/frame
  • ChemistryAndGrid: ~2 ms (38%)
  • Constraint Solver × 6: ~1.3 ms (26%)
  • Compute-bound: 87% · Memory: 9% · IO: 4%

Desktop (Ryzen 5600X, uncapped): 250 particles @ 180 FPS · 1200 particles @ 65 FPS · 3000 particles @ ~20 FPS, Brave Browser, 0 competing Tabs, 32 GB DDR5 Memory Mobile (Snapdragon 8 Gen 3): 80–570 particles @ 60 FPS (hardware-capped), Brave Browser, 0 competing Tabs, 12 GB RAM


🚀 Quick Start

Because the Chemical Chaos Engine adheres to a zero-dependency runtime philosophy, you do not need a build pipeline to run it.

The checked-in demo loads minified assets by default (for example css/style.min.css and js/*.min.js). The readable *-org.js source files are included next to them and can be regenerated with php build_chaos.php when PHP is available.

Option A: Just Open in Browser

git clone https://github.com/gotili/chemical_chaos_engine.git
cd chemical_chaos_engine_git
# Open index.html directly in any modern browser (Chrome, Firefox, Edge)

(If documentation overlays fail to load over file://, use a local server:)

npx serve .

Option B: Run Headless Tests (Node.js)

npm install      # Installs devDependencies only: jsdom + ESLint (zero runtime deps added)
npm test         # Runs the headless public-API and invariant smoke suite in Node.js

Option C: Lint the Codebase

npx eslint js/   # Flags critical issues; tolerates HPC-optimized code patterns

To interact with the running engine:
Open the HUD (⚙ icon) in the bottom-right corner. Pin it open (📌) to adjust temperature (0–400 K), reactivity (0–3.0), viscosity, turbulence, atom spawn ratios, and toggle orbital/charge visualizations. Use the Interaction Tool dropdown to switch between 6 cursor modes: Grab, Push, Pull, Cut, Destroy, and Push+Destroy.


🧠 Architecture Deep-Dive

Hybrid OOP-SoA Memory Model

The engine uses a Hybrid OOP-SoA (Structure of Arrays) architecture — not a pure one or the other.

  • Particle objects (_chemAtoms) hold metadata, bond topology, and chemistry state (type, bonds[], formal charge, isHydronium, etc.). The developer-facing CHAOS_API interacts with these.
  • Typed Array buffers hold the high-frequency physics data that the inner loop reads 60× per second.
// HIGH-FREQUENCY DATA — typed arrays (CPU cache-friendly, GC-invisible)
const _physicsBuf   = new Float32Array(MAX_SOA_PARTICLES * 4); // INTERLEAVED: [x, y, vx, vy]
const _typeBuf      = new Uint8Array(MAX_SOA_PARTICLES);        // element type index
const _flagsBuf     = new Uint32Array(MAX_SOA_PARTICLES);       // bitmask: isAtom | isRadical | isAromatic...
const _invMBuf      = new Float32Array(MAX_SOA_PARTICLES);      // 1/mass (pre-inverted)
const _radBuf       = new Float32Array(MAX_SOA_PARTICLES);      // collision radius

// LOW-FREQUENCY DATA — JavaScript objects (clean API, readable code)
// particle.x, particle.bonds, particle.isHydronium, particle.formalCharge, ...

MAX_SOA_PARTICLES = 15 000. Every frame: syncToSoA() → physics loop → syncFromSoA(). No allocations in between.

Per-Frame Chemistry Index: Before each reaction pass, a single O(N) gather populates element-specific registries (window._chemC, window._chemO, window._chemN, etc.). Organic reaction solvers iterate only over their element type — an O-solver never touches Carbon atoms. This dramatically reduces the constant factor of the chemistry step.

Spark/Effect Object Pooling: getSpark() / recycleSpark() eliminate GC stutter during explosions. Previously, visual effects allocated hundreds of new objects per detonation, causing frame drops ~3 seconds later. Pooling eliminates this entirely.

Module Architecture

Module Size Responsibility
chaos-engine-core.js ~318 KB Physics loop, SoA buffers, spatial grid, bond solver, constraint solver, CHAOS_API
chaos-organic-reactions.js ~177 KB ~30 organic reaction pathways (SN1/SN2, Aldol, Diels-Alder, acid-base, tautomerism, ...)
chaos-chemistry-logic.js ~25 KB Valency tables, bond order, electronegativity, hybridization, formal charge calculation
chaos-molecules.js ~107 KB 1707-molecule Hill-notation database
chaos-events.js ~125 KB Achievement system (50+ milestones)
chaos-spawner.js ~15 KB Molecule spawner templates & presets
chaos-orbital-viz-org.js ~22 KB sp³/sp²/sp orbital probability cloud renderer
chaos-math-utils-org.js ~8 KB Fast approximations, sanitizers, trig LUTs, vector helpers
chaos-engine-ui.js ~76 KB Engine-facing HUD bindings (sliders, toggles)
ui-core-org.js shared UI shell Parent-compatible UI helpers copied from the main website
standalone-docs-org.js release adapter Local/file-url documentation overlay path bridge

🔬 Physics & Chemistry Loop

sequenceDiagram
    autonumber
    participant Browser as Browser Frame
    participant UI as HUD / User Input
    participant Core as Engine Core
    participant SoA as SoA Buffers
    participant Grid as Spatial Grid
    participant Chem as Chemistry Solver
    participant Organic as Organic Reactions
    participant Solver as Force and Constraint Solvers
    participant Draw as Canvas2D Draw

    rect rgb(40, 40, 60)
    Note over Browser,Draw: Render task
    Browser->>Core: requestAnimationFrame animate(timestamp)
    UI->>Core: Input state is already captured
    Core->>UI: updateFPS() and updatePH()
    Core->>Draw: draw current particle state
    Draw->>Draw: Background, grid, flow field, redox overlays
    Draw->>Draw: Cull visible particles and batch bonds by atom type
    Draw->>Draw: Draw bonds, atoms, orbitals, labels, sparks, ripples
    Core-->>Browser: setTimeout(updatePhysics, 0)
    end

    rect rgb(25, 45, 65)
    Note over Browser,Grid: Physics task and SoA hot path
    Browser->>Core: updatePhysics task
    Core->>SoA: syncToSoA() copies particle objects to typed arrays
    Core->>Core: Advance frame counters and refresh dirty topology
    Core->>SoA: Integrate motion with environmental forces
    SoA->>Grid: Rebuild spatial hash buckets
    SoA->>Chem: Gather atom registries every 2 frames
    SoA->>Core: syncFromSoA() for object-facing chemistry and trails
    end

    rect rgb(55, 40, 25)
    Note over Core,Organic: Frame-sliced chemistry schedule
    Core->>Chem: Run scheduled chemistry passes
    Chem->>Chem: pH, charges, radicals, structure and molecule passes
    Chem->>Organic: Dispatch scheduled organic solvers
    Organic->>Core: Create or break bonds and mark topology dirty
    end

    rect rgb(25, 55, 35)
    Note over Core,Solver: Forces, constraints, and final sync
    Core->>Solver: Apply molecular forces, bonding, constraints, geometry fixes
    Solver->>SoA: Write corrected positions and velocities
    SoA->>Core: syncFromSoA() publishes final object state
    end
Loading

⚗️ Implemented Reaction Types

Reactions are stochastically evaluated every 10 frames (6×/second at 60 FPS), decoupled from frame rate. All probabilities scale with local temperature and the global reactivity multiplier (0–3.0×).

Category Reactions
Substitution SN1 (carbocation ionization at tertiary/secondary C), SN2 (backside attack, Walden inversion)
Acid-Base (4 types) Neutralization (H₃O⁺ + OH⁻ → 2H₂O), generic proton donation (H-X + :B), HCl strong-acid dissociation, water auto-ionization (H₂O + H₂O ⇌ H₃O⁺ + OH⁻)
Elimination E1/E2 thermal elimination (>300 K) — strips adjacent H and Cl to form C=C
Rearrangements Keto-Enol tautomerism, cumulene → alkyne (1,3-H shift), Cope sigmatropic [3,3]
Pericyclic Diels-Alder [4+2] cycloaddition
Addition Electrophilic addition (Markovnikov), alkene hydration, epoxidation, hydrogenation
Elimination/Condensation Aldol condensation, Claisen condensation, esterification, amide/peptide bond formation, alcohol dehydration
Radical Free-radical polymerization (initiation → propagation → termination), radical halogenation
Redox Combustion, oxidation/reduction, disulfide bridge formation (S-S)
Aromatic EAS (electrophilic aromatic substitution), SNAr
Organometallic/Carbonyl Michael addition (1,4-conjugate), Wittig reaction (P-ylide → C=C)
Biological Phosphorylation, thioester exchange, imine formation
Special Halohydrin formation, Cannizzaro (balanced), ring closure, hydrolysis
Geometric Strain 3-membered ring (Baeyer strain, 70% auto-break), 4-membered ring (1% torsional), cumulene instability

🧪 Testing

The test suite validates core physical and chemical invariants — without ever opening a browser. A jsdom wrapper (tests/run-node.js) mocks Canvas, ResizeObserver, and requestAnimationFrame, booting the full engine headlessly in Node.js.

npm test
=== Engine Initialization ===
  ✓ should initialize the global CHAOS_API
  ✓ should create the global typed array buffers (SoA Architecture)

=== 1. Physics & SoA Data Integrity ===
  ✓ should correctly update particle positions based on velocity
  ✓ should synchronize boolean flags to SoA bitmask

=== 2. Basic Chemical Laws ===
  ✓ should enforce steric hindrance (repulsion) between non-bonded atoms

=== 3. Organic Reactions ===
  ✓ should form a covalent bond between two close radical atoms

=== 4. Thermodynamics ===
  ✓ should increase kinetic energy when temperature is raised

Results: 7 / 7 tests passed.

The browser-based runner at tests/index.html also remains available for interactive visual output.


🔧 Design Decisions (Under the Hood)

1. Hybrid SoA — Why Not Pure OOP or Pure SoA?

Pure OOP (an array of {x, y, vx, vy} objects) forces V8 into hidden class lookups during the inner loop — constant cache misses under thousands of particles. Pure SoA (only typed arrays) would make bond topology and chemistry logic a maintenance nightmare.

The hybrid solution: slow-path object API + fast-path typed arrays. Particle objects hold bond topology, chemistry state, and all developer-facing properties. Typed arrays hold only the 4 high-frequency physics values. The syncToSoA() / syncFromSoA() bridge runs once per frame. Result: near-native inner loop speed with a clean, readable chemistry API. MAX_SOA_PARTICLES = 15 000 is pre-allocated at startup — the physics loop never touches the heap.

2. Zero-Allocation Inner Loop

Inside updatePhysics(), no JavaScript heap allocations occur. Temporary storage uses pre-allocated scratchpad buffers (_scratch, _scratch2). Bond arrays are fixed-size typed arrays. Spark/ripple visual effects use an object pool (getSpark() / recycleSpark()) — explosions that previously caused GC frame drops ~3 seconds later are now completely smooth.

If you contribute, this rule is non-negotiable: a single new Array() inside the physics tick causes a measurable frame-rate drop under V8's JIT.

3. Stochastic Chemistry — A Deliberate Trade-off

The engine evaluates reactions probabilistically, not via activation energy thresholds. This is a deliberate design choice: it allows the reaction system to be fully parameterizable via the reactivity slider (0–3.0×), produces visually compelling emergent behavior, and avoids the computational cost of continuous energy surface evaluation.

The trade-off is physical accuracy: the engine is a heuristic chemistry simulator, not a molecular dynamics code in the quantum sense. The Temporal Amortization Scheduler frames chemistry work across a 60-frame cycle to maintain the render budget, decoupling reaction evaluation from frame rate.

4. Deterministic PRNG + Reproducible Seeds

The engine uses a 32-bit xorshift-multiply PRNG (not Math.random()) for all stochastic reactions. This makes simulations reproducible: exporting a snapshot via CHAOS_API.exportConfig() includes the PRNG seed, allowing any state to be precisely restored with importConfig(). The Base64-encoded snapshot captures all parameter values, particle positions, velocities, and bond topology.


🛠️ Contribution Guidelines

Contributions are welcome. Because the simulation loop is performance-sensitive, please keep changes small, documented, and covered by tests where behavior changes.

Core architectural rules:

  1. Zero Allocation in Hot Loops: Inside updatePhysics() and reaction solvers, never use new Object(), new Array(), Array.prototype.map, or string concatenation. Use _scratch / _scratch2 scratchpads.
  2. Typed Arrays First: New particle state must be encoded as bitmasks in _flagsBuf. Do not read particle.someProperty inside physics hot loops.
  3. Module Boundaries: New organic reactions → chaos-organic-reactions.js. New chemistry logic → chaos-chemistry-logic.js. Do not extend chaos-engine-core.js with higher-level chemistry.
  4. No Object.keys() in Hot Loops: Do not use Object.keys() or array.filter() inside updatePhysics() or draw(). New chemical properties belong in _chemAtoms objects — not new TypedArrays, unless accessed >10 000 times per frame.
  5. Documentation: Any new simulation mechanic must be documented with JSDoc-style comments explaining the chemical or physical reasoning behind the logic — not just what the code does.
  6. Run Tests: Execute npm test before any commit. New reaction types must include a corresponding test case in tests/engine.test.js.

See CONTRIBUTING.md for bug reporting templates and issue guidelines.


📚 Related Documents

Document Purpose
docs/chaos-engine.html Full interactive documentation (Overview, Chemistry, Internals, Advanced)
docs/BENCHMARK_RESULTS.md Detailed performance benchmark methodology and raw numbers
docs/HPC_HANDOFF_NOTES.md Core architectural decisions and maintenance warnings
CONTRIBUTING.md Bug reporting and code standards
CITATION.cff Machine-readable citation metadata

📖 Citation

If you use the Chemical Chaos Engine in your research or publications, please cite it as:

@software{bourier2026chaos,
  author    = {Bourier, Dr. Felix Sébastien},
  title     = {Chemical Chaos Engine: 2D High-Performance Browser-Based Molecular Dynamics},
  year      = {2026},
  version   = {32.0.0},
  url       = {https://bourier.biz}
}

See CITATION.cff for the full machine-readable citation.


📄 License

Released under the MIT License. Copyright © 2026 Dr. Felix Sébastien Bourier.


Chemistry doesn't belong in a generic physics engine.

About

A heuristic interactive 2D molecular chemistry simulation.

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages