Skip to main content
Version: 0.2

Signal pool

Travelling signals are the part of the simulation that changes every frame. They are stored in a struct-of-arrays (SoA) SignalPool rather than an array of structs, which keeps each field contiguous in memory and cache-friendly.

Layout

The pool exposes its fields to JavaScript as raw pointers (the Brain wraps them in typed arrays):

FieldPointer getterMeaning
positionsparticle_positions_ptrXYZ of each signal (written each frame).
alivesignal_alive_ptrLiveness flag per slot.
axon_idxsignal_axon_idx_ptrWhich axon the signal travels.
tsignal_t_ptrPosition along the axon, 0..1.
speedsignal_speed_ptrTravel speed.
starting_pointsignal_starting_point_ptrWhich end (A or B) it started from.

The pool is created with capacity limit_signals (default 65_536):

rust/brain_sim/src/lib.rs
let pool = SignalPool::new(settings.limit_signals as u32);

The number of active signals is bounded separately by current_max_signals (default 16_000), which is the live cap you can change at runtime. Free slots are tracked with a free list so acquiring and releasing signals is cheap.

The per-frame step

step_cpu(dt) calls step_inner(dt, write_positions = true), which:

  1. Fires neurons: neurons that received a signal last frame may fire, releasing new signals onto their connected axons.
  2. Seeds when empty: if the pool has no active signals, it seeds some so the visualization never goes dark.
  3. Advances each live signal: increments t by speed * dt along its axon.
  4. Samples the Bezier curve: converts t into an XYZ position and writes it into the positions buffer (this is the per-frame write the renderer reads back).
  5. Detects arrival: when t reaches the end, the signal is released back to the free list and the destination neuron is marked as having received a signal (which can cause it to fire next frame).

step_t_only(dt) runs the same lifecycle but skips step 4, leaving the position write to a hypothetical GPU pass. The current front end always uses step_cpu.

Speed range

Signal speed is drawn from a configurable range. The Brain defaults are signalMinSpeed = 2.3 and signalMaxSpeed = 5.0; the GUI enforces min <= max. Changing these routes through World::set_setting:

src/brain.ts
this.world.set_setting('signalMinSpeed', this.settings.signalMinSpeed)
this.world.set_setting('signalMaxSpeed', this.settings.signalMaxSpeed)

Active-signal cap

set_setting('currentMaxSignals', value) clamps the live cap to never exceed the hard limit_signals:

rust/brain_sim/src/lib.rs
self.settings.current_max_signals = (value as usize).min(self.settings.limit_signals);

This cap also feeds the telemetry "active load" calculation: see Telemetry.