№ 002Interactive
From rookie to boss bird: 50 birds teach themselves Flappy Bird
Give each of fifty birds a brain of only six weights, add survival of the fittest, and usually within twenty-odd generations they clear a hundred pipes in a row. Then open that brain and see what it actually learned.
- Published
- Updated
- Reading time
- 4 min
The network in the previous entry was trained with gradient descent: measure the error, then push every weight a little in the right direction. This entry uses none of that. There are no gradients here, no backpropagation and no loss function. The only feedback is how long a bird stayed alive.
The fifty birds below are evolving in your browser right now. Watch them hit the wall for a few generations, then turn the speed up.
The leader's brain
━ positive weight━ negative weight
Pipes cleared by each generation (100 and the flock graduates)
The first generation is still flying
A brain of six numbers
The drawing on the right of the instrument is everything a bird has:
- Two inputs: the bird's height and the height of the next opening.
- Two hidden neurons.
- One output: above 0.5, the bird flaps.
The network has no biases, so its connections carry six weights in all: 2 × 2 + 2 × 1. Putting a neural network and evolution together is called neuroevolution. Instead of computing how each weight should change, you keep a crowd of individuals with different weights and let the ones that do well leave offspring.
How one generation becomes the next
In the first generation all six weights of all fifty birds are random, and most birds hit the wall straight away.
const population = new Population({
shape: [2, 2, 1], // 2 inputs, 2 hidden neurons, 1 output
size: 50,
});Once every bird is dead, they are ranked by how long they lived, and the next generation is produced:
elitism: 0.2, // the best 20% are kept unchanged
randomRate: 0.2, // 20% are brand-new random individuals, so the flock can't get stuck on one habit
// the remaining 60% are children of the birds that were kept"Children" means something plain. A bird's genome is that array of six numbers. Two birds make a chick by taking each position from one parent or the other with equal probability; each number then has a 10% chance of being nudged at random by at most ±0.5:
private breed(a: Float32Array, b: Float32Array): Float32Array {
const child = Float32Array.from(a);
for (let i = 0; i < child.length; i++) {
if (rng() <= 0.5) child[i] = b[i]; // crossover: 50% chance of the other parent's number
if (rng() <= mutationRate) child[i] += rng() * mutationRange * 2 - mutationRange; // mutation
}
return child;
}Those three pieces of code are the whole algorithm.
Open the brain: what it learned is a subtraction
Once the birds fly well, look at the drawing on the right of the instrument. Brains that evolved successfully tend to look alike: at least one hidden neuron receives "bird height" and "gap height" through lines of opposite colours, one positive and one negative.
One positive plus one negative computes "my height minus the height of the opening": am I above the opening or below it? Below, so flap. This is no coincidence. Both inputs are positive numbers between 0 and 1, so a neuron whose two weights share a sign can only measure how large the two are together; it cannot tell which is higher. Comparing them takes one positive and one negative.
Six numbers, and what they learned is a subtraction. That subtraction is not in any line of code. It is simply the one combination of six numbers, out of all of them, that lived longest.
Evolution doesn't guarantee success
I ran 12 different random seeds for 60 generations each: 10 graduated within 22 generations, one took until generation 43, and in one the whole population got stuck in the same bad habit and never cleared more than 3 pipes in all 60 generations. With 40 seeds, 5 never learned.
This is the usual weakness of evolutionary methods. With no gradient to say which way is better, evolution can only stumble on better individuals by luck. If the whole flock looks alike and all of it is bad, the 20% of fresh blood takes a long time to turn things round. If the score refuses to climb, press Restart and begin with a new set of ancestors.
It works here because the problem is small: two inputs, one action, six numbers. The space to search is small enough that shooting blind still hits. With more parameters, the chance of stumbling on a good solution falls quickly, and that is usually where gradient descent wins.
Further reading
- NEAT (NeuroEvolution of Augmenting Topologies): Wikipedia entry. The network in this entry has a fixed shape and only its weights evolve; NEAT evolves the shape as well.
- Nils J. Nilsson, The Quest for Artificial Intelligence
- Dan Simon, Evolutionary Optimization Algorithms
This article first appeared on my previous site in March 2025. The interactive parts were rewritten from scratch in TypeScript when it moved into this notebook, and the text was rewritten in September 2026.