BL-1 is a JAX-based framework for simulating dissociated cortical cultures growing on multi-electrode arrays (MEAs). It combines biologically detailed spiking neuron models with conductance-based synapses, four timescales of synaptic plasticity, virtual MEA recording and stimulation, and closed-loop game experiments -- all within a fully differentiable, JIT-compiled simulation loop built on jax.lax.scan. BL-1 enables researchers to run in-silico replications of biological intelligence experiments at scale, with GPU acceleration and support for gradient-based optimization through surrogate gradients. The closed-loop game module replicates the DishBrain experiment (Kagan et al. 2022), which grew biological cortical neurons on HD-MEAs and demonstrated that living neural cultures could learn to play Pong via free-energy-principle feedback.
- Izhikevich (2003) -- five cortical cell types: Regular Spiking (RS), Intrinsically Bursting (IB), Chattering (CH), Fast Spiking (FS), Low-Threshold Spiking (LTS)
- Adaptive Exponential (AdEx) integrate-and-fire model
- Hybrid populations mixing Izhikevich and AdEx neurons
- Jaxley adapter for morphologically detailed compartmental neurons (optional)
- AMPA -- fast excitatory (tau = 2 ms)
- NMDA -- slow excitatory with Mg2+ voltage-dependent block (dual-exponential kinetics)
- GABA_A -- fast inhibitory (tau = 6 ms)
- GABA_B -- slow inhibitory (dual-exponential kinetics)
- Short-term plasticity (STP) -- Tsodyks-Markram depression and facilitation
- Spike-timing-dependent plasticity (STDP) -- asymmetric Hebbian learning with configurable time constants
- Homeostatic scaling -- firing-rate-based synaptic weight normalization
- Structural plasticity -- activity-dependent synapse creation and pruning
- CL1 64-channel -- 8x8 grid, 200 um spacing (standard configuration)
- MaxOne HD-MEA -- 26,400 electrodes, 17.5 um spacing (high-density mode with sparse indexing)
- Spike detection, LFP approximation, and electrical stimulation
- Configurable detection and activation radii
- Pong -- closed-loop paddle game replicating the biological DishBrain experiment (Kagan et al. 2022) with FEP feedback
- Doom -- ViZDoom integration for 3D environments (optional
vizdoomextra) - Three feedback modes: Free Energy Principle (
fep),open_loop,silent
- Criticality -- branching ratio, neuronal avalanche size distributions
- Burst detection -- network burst identification, inter-burst intervals, recruitment statistics
- Functional connectivity -- cross-correlation, transfer entropy, small-world and rich-club coefficients
- Information theory -- active information storage, mutual information, integration, complexity
- Pharmacology -- drug effect modelling (TTX, Carbamazepine, Bicuculline, APV, CNQX)
- Sensitivity analysis -- parameter sweeps, fitting, firing rate and synchrony metrics
- Spike raster plots with E/I coloring
- Population firing rate traces
- MEA heatmaps and activity plots
- Avalanche distribution plots
- Burst overlays and ISI distributions
- Entire simulation loop JIT-compiled via
jax.lax.scan-- zero Python overhead - Sparse connectivity with JAX BCOO and event-driven operations
- Fast sparse path with
segment_sumfor 50K+ neuron networks - Pallas custom kernels for CSC event-driven synaptic input (GPU)
- GPU-ready: 5.3x realtime at 10K neurons on A100
- Differentiable through surrogate gradients (SuperSpike, sigmoid, arctan)
BL-1 integrates with doom-neuron to create an in-silico replication of the biological DishBrain DOOM experiment. BL-1's virtual CL1 server replaces the real Cortical Labs hardware with 10,000 biophysically grounded spiking neurons -- speaking the exact same UDP protocol -- so the PPO training system connects without modification.
Live 4-panel dashboard showing the closed-loop system in real-time:
- DOOM Gameplay -- first-person view from VizDoom
- Neural Activity -- spike raster, mountain plot, MEA heatmap, and firing rate timeseries from the simulated culture (RatInABox-inspired composable monitoring)
- Decoder Inference -- action probability compass and per-head bar chart showing what the ML decoder infers from the neural spikes
- Automap (2D) -- retro Atari-style top-down map view with pixelated rendering
DOOM Game State ─► Encoder (PyTorch) ─► Stimulation frequencies/amplitudes
│
UDP :12345 (72 bytes)
│
▼
BL-1 Virtual CL1 (JAX)
10,000 Izhikevich neurons
AMPA/NMDA/GABA synapses + STP
64-channel virtual MEA
│
UDP :12346 (40 bytes)
│
Reward ◄── DOOM executes ◄── Decoder (PyTorch) ◄── Spike counts per channel
The encoder learns stimulation patterns via REINFORCE (non-differentiable spikes). The decoder maps 8 channel-group spike counts to movement, camera, and attack actions. Feedback stimulation rewards kills and punishes damage, scaled by TD-error surprise.
doom-neuron's SDK mode generates random spikes. BL-1 provides neurons that actually respond to stimulation with structured dynamics -- validated against Wagenaar et al. (2006) cortical culture recordings. Ablation tests (--decoder-ablation zero) confirm the simulated neurons carry meaningful signal.
# Clone both repos
git clone https://github.com/m9h/bl1.git
git clone https://github.com/SeanCole02/doom-neuron.git
# Shared venv
uv venv .venv --python 3.12 && source .venv/bin/activate
uv pip install -e "./bl1[vizdoom,dev]"
uv pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
uv pip install tensorboard==2.20.0 tables opencv-python
# Launch (BL-1 virtual CL1 + doom-neuron training)
./run_bl1_doom.sh
# Open dashboard in browser
# doom-neuron/visualisation.htmlSee INTEGRATION.md for full documentation.
A RatInABox-inspired monitoring system with composable plot methods:
from bl1.monitor import ActivityMonitor
mon = ActivityMonitor(n_neurons=10_000)
# Composable single-panel (RatInABox pattern)
fig, ax = mon.plot_raster(window_s=10.0)
fig, ax = mon.plot_mountain(window_s=10.0) # stacked filled-area
fig, ax = mon.plot_mea_heatmap(window_s=1.0) # 8x8 electrode grid
# Full dashboard
fig, axes = mon.plot_dashboard(window_s=10.0)
# MJPEG streaming for browser
from bl1.monitor import NeuralMJPEGServer
server = NeuralMJPEGServer(port=12350)
server.start()
server.update_frame(mon.render_frame(640, 480))From source (recommended):
git clone https://github.com/your-org/bl1.git
cd bl1
pip install -e ".[dev]"With optional extras:
# Development tools (pytest, hypothesis, ipykernel)
pip install -e ".[dev]"
# Code quality (ruff, mypy, vulture, radon, bandit, interrogate)
pip install -e ".[quality]"
# Documentation (sphinx, RTD theme)
pip install -e ".[docs]"
# Morphological neurons via Jaxley
pip install -e ".[jaxley]"
# Doom game environment
pip install -e ".[vizdoom]"JAX GPU support:
BL-1 uses CPU JAX by default. For GPU acceleration, install the appropriate JAX variant for your CUDA version:
pip install -U "jax[cuda12]"See the JAX installation guide for details.
import jax
import jax.numpy as jnp
from bl1 import Culture
from bl1.core import simulate, create_synapse_state
from bl1.visualization import plot_raster
# 1. Create a culture: 200 neurons on a 3x3 mm substrate
key = jax.random.PRNGKey(42)
net_params, state, izh_params = Culture.create(
key, n_neurons=200, ei_ratio=0.8, g_exc=0.05, g_inh=0.20,
)
# 2. Prepare synapse state and external drive
n_neurons = 200
syn_state = create_synapse_state(n_neurons)
T_ms, dt = 1000.0, 0.5 # 1 second simulation
n_steps = int(T_ms / dt)
# Background noise current (keeps the culture spontaneously active)
I_ext = 5.0 * jax.random.normal(jax.random.PRNGKey(0), (n_steps, n_neurons))
# 3. Build initial neuron state from the culture state
from bl1.core import NeuronState
neuron_state = NeuronState(v=state.v, u=state.u, spikes=state.spikes)
# 4. Run simulation
result = simulate(
izh_params, neuron_state, syn_state, stdp_state=None,
W_exc=net_params.W_exc, W_inh=net_params.W_inh,
I_external=I_ext, dt=dt,
)
# 5. Plot the spike raster
n_exc = int(n_neurons * 0.8)
fig = plot_raster(result.spike_history, dt_ms=dt, ei_boundary=n_exc)
fig.savefig("raster.png", dpi=150) +------------------+
| Closed-Loop | bl1.loop
| Controller |
+--------+---------+
|
encode / decode / stimulate
|
+--------------+--------------+
| Virtual MEA | bl1.mea
| (64-ch / HD-MEA / LFP) |
+--------------+--------------+
|
record spikes | inject current
|
+---------------------------+---------------------------+
| Network Layer | bl1.network
| place_neurons | build_connectivity | Culture |
+---------------------------+---------------------------+
|
+------------------+------------------+
| Plasticity Stack | bl1.plasticity
| STP | STDP | Homeostatic | |
+------------------+------------------+
|
+-----------------------+-----------------------+
| Core Simulation | bl1.core
| Izhikevich / AdEx neurons |
| AMPA / NMDA / GABA_A / GABA_B synapses |
| simulate() via jax.lax.scan |
+-----------------------------------------------+
Data flow per timestep: Neurons fire spikes. Spikes propagate through conductance-based synapses (optionally modulated by STP). STDP and homeostatic rules update weights. The MEA records spikes and injects stimulation currents. In closed-loop mode, the controller decodes motor actions from neural activity, steps the game, encodes sensory feedback, and delivers stimulation according to the selected feedback policy.
BL-1 uses YAML configuration files to define experiment parameters. Three reference configs are included:
| Config | File | Description |
|---|---|---|
| Default | configs/default.yaml |
100K neurons, standard Izhikevich parameters, 64-ch MEA |
| Wagenaar Calibrated | configs/wagenaar_calibrated.yaml |
5K neurons tuned to reproduce Wagenaar et al. (2006) spontaneous bursting (~8 bursts/min, IBI ~9s) |
| DishBrain Pong | configs/dishbrain_pong.yaml |
In-silico replication of the biological DishBrain Pong experiment with FEP feedback |
Load a config:
import yaml
with open("configs/wagenaar_calibrated.yaml") as f:
cfg = yaml.safe_load(f)
net_params, state, izh_params = Culture.create(
key,
n_neurons=cfg["culture"]["n_neurons"],
ei_ratio=cfg["culture"]["ei_ratio"],
lambda_um=cfg["culture"]["lambda_um"],
p_max=cfg["culture"]["p_max"],
g_exc=cfg["culture"]["g_exc"],
g_inh=cfg["culture"]["g_inh"],
)- API reference:
docs/api/-- auto-generated Sphinx docs for all modules - Quick start guide:
docs/quickstart.rst - Notebooks:
notebooks/00_quickstart.ipynb-- interactive tutorial
Build the HTML docs locally:
make docs
# Output: docs/_build/html/index.htmlBL-1 uses a Makefile-driven workflow with Ruff for linting and formatting, mypy for type checking, and pytest for testing.
# Lint and format check
make lint
# Auto-fix lint issues
make lint-fix
# Type checking
make typecheck
# Run tests (excludes slow calibration tests)
make test
# Tests with coverage report
make coverage
# Code quality metrics (docstring coverage, dead code, complexity, security)
make quality
# All checks
make all
# Benchmarks (local CPU / Modal A100)
make benchmark
make benchmark-gpuBL-1 is released under the MIT License.
