Train a CTRNN with Manual BPTT

Browser-only training: HTML + CSS + JavaScript. No PyTorch, no TensorFlow, no automatic differentiation.

Training mode
Browser-only Manual BPTT SGD + gradient clipping Live recurrent dynamics
ht+1 = ht + (dt/τ)[−ht + tanh(Wrecht + Winxt + b)]
yt = Woutht+1 + bout

We numerically discretize the continuous-time equation with Euler's method. BPTT then differentiates through this entire sequence of Euler steps.

1. The task: delayed response

Input channel x₁ receives a pulse from 1 to 2 s. The desired response appears later on output channel y₁, from 3 to 5 s. Any additional input and output channels are present but set to zero for this task.

x₁(t): pulse → recurrent memory → target y₁(t): delayed pulse

3. What is training doing?

Epoch 0 / 1500
MSE loss —
Gradient norm —
Status Ready
① Forward simulate h(t) and y(t)
→
② Loss compare y(t) with target
→
③ BPTT send error backward through time
→
④ Update W ← W − η∇W
What does “backward through time” mean?

h(t) influences h(t+dt), which influences later states. Therefore an output error at a late time can be caused by recurrent activity much earlier in the trial. BPTT follows these causal links backward through the unrolled Euler steps.

4. Output: prediction vs target

Black = trained network output. Dashed = desired target. Light gray = input pulse.

5. Training loss

MSE should generally decrease as the weights learn the task.

6. Hidden-unit activity

These four curves are the learned internal recurrent state h₁(t), …, h₄(t).

7. Population trajectory

PCA projects the 4D hidden trajectory into PC1 and PC2. Green = trial start, black = trial end.

8. Learned network

Blue = positive weight, orange = negative weight, line thickness = |weight|. These lines change during training because SGD changes the matrices.

9. Weight matrices

These are the current learned parameters. The values update during training, so after training you can inspect exactly what the network learned.

Win

Input → hidden weights

Wrec

Hidden → hidden recurrent weights

Wout

Hidden → output weights

Biases b and bout

Hidden and output bias terms

negative near zero positive
10. BPTT mathematics implemented in JavaScript

9. The BPTT mathematics implemented in JavaScript

Forward Euler step

α = dt/τ
at = Wrecht + Winxt + b
ht+1 = (1−α)ht + α tanh(at)

Loss

L = (1/T) Σt(yt − yttarget)²

Backward recurrent signal

qt = α [∂L/∂ht+1] ⊙ tanh′(at)
∂L/∂Wrec += qthtT

SGD update

W ← W − η ∂L/∂W

The code computes these derivatives explicitly. There is no autograd engine.

Important note

The current setup keeps the task fixed while allowing the architecture to change. The delayed-response task remains fixed so architecture changes can be compared cleanly. Later versions can expose task timing, activation, optimizer, multiple trials and task families.