Pong AI

This is a demo of an AI that learns to play Pong using a multi-layer perceptron (MLP) evolved via an evolutionary algorithm.

The debug information above each agent shows the 21 neurons that make up its "brain".

Press 'n' to hide the debug information.
Press 'p' to cycle between three control modes: AI vs AI, AI vs Human, and Human vs Human.
Control the left paddle with the W and S keys.
Control the right paddle with the ↑ and ↓ arrow keys.

See if you can beat the AI you trained!

The code is available on GitHub.

Technical details:
  • Rendering is done with WebGL (more portable than WebGPU at this stage) using Macroquad
  • The AI is implemented in Rust, compiled to WebAssembly (WASM) to run in the browser.
  • The neural network is a 3-layer MLP with 8-16-4-1 layer arrangement.
  • The genetic algorithm consists of 128 individuals in each generation. Training in the browser runs for 100 generations, with full round robin.
  • The mutation strategy is random gaussian mutation with no crossover.
  • Hidden layer activations are rectified linear units (ReLU).
  • The output layer activation is a hyperbolic tangent (tanh).
  • The fitness function is zero-sum score total from the round.
  • Pre-trained champion:
    • I've pre-seeded the neural network with a locally trained champion (to save your CPU). But the generations you see printed to the screen, and the final result, is AI training happening in your browser in WASM (with a bit of JavaScript glue!)
    • The "pre-trained" champion is the result of a few thousand generations of training on a local machine with tournament selection, different fitness functions (e.g. number of returns, number of "shots on goal", time alive, etc.), using a variety of mutation strategies (including single-point crossover and uniform mutation with different strengths and rates). It even went through a GPU phase with Torch, then WebGPU w/ GLSL compute shaders, but ultimately found the most success with simple multithreaded CPU training. Turns out pong isn't that much faster on kernels.
Acknowledgements:

This project revisits a formative collaboration with a former peer whose early contributions shaped its trajectory. The original implementation—a C++ genetic algorithm with a neural network to learn Pong—was largely his work. At the time, I lacked the technical depth to fully grasp it. He brought competitive programming rigor and algorithmic fluency that exceeded my understanding then.

Rebuilding it now in Rust serves a dual purpose: technical redemption and continued learning. The performance gains from rustc and LLVM's modern optimization pipeline—including SIMD auto-vectorization—have made the reimplementation orders of magnitude faster. More importantly, it’s been an exercise in understanding compilation, lowering, and systems-level design—concepts I hadn’t appreciated during the original effort.

The broader arc is about catching up with the field. Back in 2015, transformers didn’t exist in the mainstream curriculum. Reconstructing this project from first principles is a way to retrace the path from classical neural networks to the architectures that define modern AI—following the breadcrumbs, step by step.