This repository contains a single-page, browser-based demo of a recurrent neural network laid out as a stack of 2D sheets of neurons.
Each sheet is a 60×60 grid of simple discrete-time neurons with either:
- tanh nonlinearity, or
-
sigmoid nonlinearity mapped to
$[-1, 1]$ ,
and with local topographic connectivity, random recurrent connections, and feedforward / feedback coupling across three layers.
The goal is to make the dynamics visible and explorable in real time: you can poke neurons, watch their traces, change connectivity structure, and inspect a compressed adjacency matrix on the fly.
- Clone the repo or download the HTML file (e.g.
index.html). - Open it in a modern browser (Chrome, Firefox, Safari).
- That’s it — everything runs locally in JavaScript, no build step needed.
You should see:
- On the left: a vertically stacked view of three oblique “sheets” of neurons, each shown as a heatmap (blue → gray → red).
- On the top-right of the sheet canvas: a small inset showing a compressed connectivity matrix.
- On the right side: controls, a neuron trace viewer, and a parameter dump button.
- Layers: 3
- Neurons per layer: 60×60 = 3,600
- Total neurons: 10,800
- Boundary conditions: periodic in both x and y (toroidal grid)
We index neurons as:
- layer (\ell \in {0,1,2})
- coordinates
$(x,y)) with (x,y \in {0, \dots, 59}$ - state
$a_{\ell}(x,y,t) \in [-1,1]$ at discrete time step$t$
Layer 0 is treated as the input (sensory) layer; layers 1 and 2 are progressively “deeper”.
Each neuron updates according to a leaky nonlinearity:
where:
- (\lambda \in [0,1]) is the leak (slider
Leak (update fraction λ)), - (\phi) is either:
- tanh: (\phi(x) = \tanh(x)), or
-
sigmoid mapped to ([-1,1]):
$\phi(x) = 2,\sigma(x)-1 = \frac{2}{1+e^{-x}} - 1$ ,
- (I_i(t)) is the total synaptic + external input to neuron (i).
A single “walker” performs either:
- a random walk on the grid (default), or
- is manually positioned using two sliders (X/Y),
always on layer 0 (input layer).
The walker injects a local Gaussian bump of input:
[ I^{\text{stim}}_{\ell}(x,y,t) = \begin{cases} A \exp\left( -\dfrac{(x - x_w)^2 + (y - y_w)^2}{2\sigma^2} \right), & \ell = 0 \ 0, & \text{otherwise} \end{cases} ]
where:
- (A) is
Walker stimulus strength, - (\sigma) is fixed (
sigmaStimin the code), - ((x_w, y_w)) is the walker position.
Within each layer, each neuron receives input from a local neighborhood defined by a kernel over offsets ((\Delta x, \Delta y)) in a small radius (e.g. radius = 3).
Two types of kernels are supported:
-
Gaussian kernel (normalized to sum to 1):
[ w_{\text{loc}}(\Delta x, \Delta y) \propto \exp\left(-\frac{\Delta x^2 + \Delta y^2}{2\sigma_{\text{loc}}^2}\right). ] -
Mexican-hat (center–surround) kernel, implemented as a difference of Gaussians (DoG) with zero integral: [ w_{\text{loc}} = G_{\sigma_{\text{exc}}} - B, G_{\sigma_{\text{inh}}}, ] where (B) is chosen so that the sum over the kernel is zero, then rescaled to have (\max |w_{\text{loc}}| = 1).
Local input to neuron (i = (\ell, x, y)):
[ I^{\text{local}}i(t) = g{\text{local}} \sum_{\Delta x, \Delta y} w_{\text{loc}}(\Delta x, \Delta y), a_{\ell}(x + \Delta x,, y + \Delta y,, t), ]
with toroidal wrapping in x and y. Local coupling gain (g_local) controls (g_{\text{local}}).
Each layer also has sparse random connections:
- For each neuron, an approximate in-degree (k) is determined from
Random conn. probability× number of neurons in the layer. - For each post neuron, a set of random pre neurons is chosen within the same layer.
- All random edges are strictly within-layer (assertions in the code guard against cross-layer edges).
- Random weights are drawn from ([-1,1]), optionally made strictly positive or negative to enforce Dale’s rule.
To keep dynamics more stable when density changes, weights are scaled by (1/\sqrt{k}), so the random probability slider mostly changes topology, not just effective gain.
Random input term:
[ I^{\text{rand}}i(t) = g{\text{random}} \sum_{j \in \mathcal{R}(i)} w_{ij} , a_j(t), ]
where (\mathcal{R}(i)) is the set of random presynaptic partners of neuron (i). Random connectivity gain (g_random) controls (g_{\text{random}}).
Dale’s rule (checkbox) makes each neuron either excitatory or inhibitory:
- E neurons have all outgoing random weights (\ge 0),
- I neurons have all outgoing random weights (\le 0).
Cross-layer connectivity uses the same local kernel as within-layer, but scaled by separate gains.
For neuron (i = (\ell, x, y)):
-
Feedforward (from layer (\ell-1) to (\ell), for (\ell > 0)):
[ I^{\text{ff}}i(t) = g{\text{cross}} \sum_{\Delta x, \Delta y} w_{\text{loc}}(\Delta x, \Delta y), a_{\ell - 1}(x + \Delta x, y + \Delta y, t). ]
-
Feedback (optional, from layer (\ell+1) to (\ell)):
[ I^{\text{fb}}i(t) = g{\text{back}} \sum_{\Delta x, \Delta y} w_{\text{loc}}(\Delta x, \Delta y), a_{\ell + 1}(x + \Delta x, y + \Delta y, t). ]
These are controlled by:
Feedforward gain (g_cross)((g_{\text{cross}}))Enable back projections(checkbox)Back projection gain (g_back)((g_{\text{back}}))
Putting it together, the total input to each neuron is:
[ I_i(t) = I^{\text{local}}_i(t) + I^{\text{rand}}_i(t) + I^{\text{ff}}_i(t) + I^{\text{fb}}_i(t) + I^{\text{stim}}_i(t), ]
and the state update is:
[ a_i(t+1) = (1 - \lambda), a_i(t) + \lambda, \phi(I_i(t)). ]
- Shows the 3 layers as slightly sheared “parallelogram” sheets.
- Each neuron is colored with a blue–gray–red colormap:
- blue ≈ strongly negative
- gray ≈ around 0
- red ≈ strongly positive
Layers are stacked vertically (no overlap), with:
- Layer 0 at the bottom (input layer),
- Layer 2 at the top (deepest).
A white square marks the current walker position on the input layer.
If you click on any neuron in any layer:
- That neuron is highlighted in yellow.
- Its activity trace is shown in the trace viewport on the right.
On the right, the “Neuron trace” panel shows the last ~400 time steps of the selected neuron:
- x-axis: time (recent history, rolling buffer)
- y-axis: activity ([-1,1])
- middle horizontal line: 0
- green curve: selected neuron’s activity
You can:
- change parameters while watching how single-cell dynamics react,
- click different neurons to compare behavior of:
- input vs deep layers,
- excitatory-like vs inhibitory-like cells,
- cells near vs far from the walker stimulus.
Manual walker control(checkbox)- Off: walker performs a random walk on the input layer.
- On: you control it via sliders:
Walker X positionWalker Y position
Walker stimulus strength: amplitude (A) of the Gaussian bump.
Leak (update fraction λ)- 0: states are frozen, no updates.
- 1: full nonlinearity update each step.
Use sigmoid nonlinearity- Off: use (\tanh).
- On: use logistic mapped to ([-1,1]), (2\sigma(x)-1).
Local coupling gain (g_local): scales the contribution of the local kernel.Use Mexican-hat local kernel:- Off: single Gaussian (smooth, cooperative).
- On: center–surround DoG with zero integral (pattern-forming, winner–surround-loser).
Random conn. probability: controls the density of random recurrent connections within each layer.Random connectivity gain (g_random): scales their impact.Enforce Dale's rule (per layer):- On: each neuron is classified as E or I; all its random outgoing weights share the same sign.
Regenerate random connectivity:- Re-samples the random graph and weights (respecting current probability and Dale’s rule setting).
Feedforward gain (g_cross):- Local, kernel-shaped projections from layer (\ell) to (\ell+1).
Enable back projections:- Turn on local feedback ((\ell+1 \to \ell)) using the same kernel shape.
Back projection gain (g_back):- Scales the feedback term.
These let you explore:
- purely feedforward regimes,
- recurrent hierarchies with feedback,
- how patterns propagate (or echo) through depth.
Update speed:- Number of discrete update iterations per animation frame.
- Increasing this makes the dynamics “run faster” while keeping rendering at a reasonable frame rate.
The inset on the top-right is a compressed effective connectivity matrix, computed from:
- local kernel (within and across layers),
- random connectivity,
- feedforward and feedback gains.
Key ideas:
- Neurons within each layer are grouped into bins (e.g. 24 groups per layer).
- For each pair of groups (pre, post), the code accumulates an average effective weight:
- local + random + feedforward + feedback (depending on current slider values).
- This yields a small matrix of size: [ \text{CM} = \text{LAYERS} \times \text{BINS_PER_LAYER}, ] visualized as a CM×CM heatmap.
You’ll see:
- A block structure:
- diagonal blocks = within-layer connectivity (local + random),
- off-diagonal blocks = feedforward / feedback between layers.
- Colorbar (next to it):
- red = positive effective weight,
- blue = negative,
- gray ≈ zero.
As you tweak:
g_local,g_random,g_cross,g_back,Use Mexican-hat,- or toggle feedback on/off,
you can watch the structure of the effective adjacency matrix change in real time.
The Dump params + connectivity (JSON) button:
- captures the current simulation state into a JSON file that includes:
- basic parameters (layer size, kernel settings),
- all sliders and checkbox states,
- the local kernel values (Gaussian or Mexican hat),
- the full random connectivity edge list (sparse),
- the current compressed connectivity matrix (grouped).
This makes it easy to:
- archive interesting regimes,
- analyze connectivity offline (e.g. in Python/NumPy),
- or reconstruct comparable networks in other environments.
This toy model is a nice playground for:
- Criticality and spectral radius
Tuningg_localandg_randomcan move the network from quiescent to chaotic regimes. - Pattern formation
Mexican-hat kernels naturally generate bumps, rings, and traveling patterns. - Hierarchical processing
Turn on feedforward and feedback to explore how activity propagates and echoes across layers. - Single-cell & population views
Combine the sheet view, neuron trace, and adjacency inset to relate micro (neuron-level) and macro (group-level connectivity) structure.
The code in this repository is licensed under the Apache License 2.0.
If you use this project in your own work, please include a reference such as:
Based on the "Layered Neural Sheet Demo" by Mario Negrello (Apache-2.0), developed with AI-assisted coding using ChatGPT (OpenAI).
This code was designed as an interactive educational resource for exploring dynamical behavior in simple recurrent neural sheets: local vs random connectivity, hierarchical couplings, and single-neuron activity — all in real time, in the browser.
- Concept, design and implementation: Mario Negrello
- AI-assisted iteration and code drafting: ChatGPT (GPT-5.1 Thinking, OpenAI)
Enjoy poking your little cortical sheet ✨