Networks grow their own topology to balance two poles, while species rise and fall.
NeuroEvolution of Augmenting Topologies, written from scratch, on the classic double pole benchmark: a 1 m pole and a short pole hinged on one cart, simulated with Wieland's equations at 200 Hz. Every genome starts with its inputs wired straight to the force output, and mutation can split a link with a new hidden node or add a link, each change stamped with a global innovation number so genomes line up gene by gene for crossover and speciation. Similar genomes share a species and its fitness, protecting new structure while it tunes, and stagnant species die out. Generations run headlessly; on screen the champion balances live, its network is drawn as a circuit that grows, the best of each big species balances in a gallery, and a Muller plot shows every species branching from its parent. Each time the task is solved the short pole gets longer.
Try it. Drag sideways across the canvas to shove the champion's cart and see whether its network recovers (an autopilot shoves it now and then too). Press R to restart evolution from minimal networks with a new seed.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a NEAT (NeuroEvolution of Augmenting Topologies) demo with JavaScript and the HTML canvas element, where evolving neural networks learn to balance a pole on a cart. Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.
Start simple:
- Simulate a cart on a 4.8 m track with a 1 m pole hinged on top (standard cart-pole equations, 50 steps per second). A controller pushes with up to 10 N. The run fails if the pole tips past 12 degrees or the cart leaves the track.
- A genome is a list of node ids plus connection genes (from, to, weight, enabled, innovation number). Start every genome minimal: the four inputs (cart position and velocity, pole angle and angular velocity) plus a bias wired straight to one output.
- Keep a global table of innovation numbers so the same new connection always gets the same number.
- Mutations: perturb weights, add a connection between two unconnected nodes (never creating a cycle), or add a node by splitting an existing connection in two.
- Crossover lines up two parents by innovation number. Speciate with the NEAT distance (excess and disjoint genes plus average weight difference) and divide each genome's fitness by its species size.
- Fitness is steps balanced, up to 1000. Evaluate a population of 150 without drawing, a slice per frame, and run the current champion live on screen.
Once that works, make it beautiful:
- Draw the champion's network with inputs on the left and the output on the right, hidden nodes placed by depth, and connections colored by sign with width by weight.
- Plot species sizes over generations as a stacked area chart.
- Let me drag the mouse to shove the cart and test the champion.
Explain the key ideas in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as the harder double pole version, removing the velocity inputs so it must evolve recurrent memory, or animating new nodes as they appear.