I Built a Neural Network You Can Watch Train — Forward Pass, Loss, and Backprop, Animated

# react# javascript# machinelearning# webdev
I Built a Neural Network You Can Watch Train — Forward Pass, Loss, and Backprop, AnimatedAmar Gul

Most NN explanations show the math before you ever see what happens. I built the opposite: an animated network that trains in front of you. Here's how it works and what I learned.

Every neural-network tutorial I tried threw equations at me before I ever saw what was actually happening. I wanted the reverse: watch the activations flow forward, watch the loss bars shrink, watch backprop push gradients right-to-left across the layers.

So I built it. Here's a neural network that trains itself in front of you 👇

What you're actually seeing

  • Forward pass — particles flow left → right as activations propagate through the layers.
  • Loss — bars drop each epoch, and the output neurons glow red → gold → green as the error shrinks.
  • Backpropagation — particles flow right → left, the gradient returning toward the input.
  • 9 epochs, deterministically timed so every run is identical.

No training data is harmed in the making of this animation — it's a faithful visual model of the phases, built for intuition, not for crunching MNIST.

The stack

  • React for the phase state machine: idle → forward → loss → backward → done
  • Framer Motion for the particle and node animations
  • Web Audio API for synthesized sound (no audio files), tied to the dot movement
  • A small drift-corrected timing loop so the whole run lands on a fixed wall-clock budget

Three things that were trickier than expected

1. Animate particles with CSS transforms, not SVG attributes

My first version animated each particle's cx/cy. It worked but stuttered. Switching to Framer Motion's x/y (which compile to GPU-friendly CSS transforms) made it buttery:

<motion.circle
  r={4}
  cx={0} cy={0}
  initial={{ x: x1, y: y1, opacity: 0, scale: 0 }}
  animate={{ x: [x1, x2], y: [y1, y2], opacity: [0, 1, 1, 0] }}
  transition={{ duration: 0.65, ease: "easeInOut" }}
/>
Enter fullscreen mode Exit fullscreen mode

2. Backprop has to flow backward

Sounds obvious, but my first pass spawned the backprop particles in the same direction as the forward pass. The fix was just swapping the source/target layer so the dots travel from the deeper layer back toward the input:

// Forward: layer l-1 → l   (left → right)
spawnParticles(l - 1, l, FORWARD_COLOR);

// Backprop: layer l → l-1  (right → left)
spawnParticles(l, l - 1, BACKWARD_COLOR);
Enter fullscreen mode Exit fullscreen mode

Tiny change, huge difference in how "correct" it reads.

3. Make the network show it's learning

Loss bars are fine, but I wanted the network itself to react. So the output nodes are colored by the current loss — the same thresholds as the bars, so the legend stays consistent:

function lossColor(loss) {
  if (loss < 0.15) return GREEN;   // basically trained
  if (loss < 0.40) return GOLD;
  return RED;                       // high error
}
Enter fullscreen mode Exit fullscreen mode

Early epochs glow red; by the end they settle into green. You see the network heal.

Bonus: deterministic timing (so I could record it)

setInterval drift made every recording a slightly different length. I anchored a start timestamp and held each epoch to a fixed budget, correcting drift as it goes:

function waitUntil(targetMs) {
  const remaining = targetMs - (Date.now() - runStart);
  return sleep(Math.max(remaining, 0));
}
// ...end of each epoch:
await waitUntil((epoch + 1) * EPOCH_BUDGET_MS);
Enter fullscreen mode Exit fullscreen mode

Now every run lands on the same total time regardless of frame jitter.

What I learned

  • Animation isn't decoration — putting the direction of data flow on screen taught me backprop better than any equation did.
  • Browsers block the Web Audio API until a user gesture, so "start" had to be a real click.
  • Deterministic timing is underrated: it made the thing recordable and the code simpler.

I'm animating a whole set of these — sorting, Dijkstra, hash tables, binary trees. What algorithm should I visualize next? Drop it in the comments 👇