Skip to content
rondahan.
All work
2025 - 2026

Icy Tower - Game & AI

A game written from scratch, then a network evolved to play it

179
floors reached by the AI
2,000
generations trained
0
game engines or ML libraries used

What it does

A browser remake of Icy Tower built without a game engine, and a neural network taught to play it. The game has hand-written physics, a webcam hand-tracking control mode, ghost replays of previous runs and a coin economy with unlockable characters. The AI side is a neural network evolved by a genetic algorithm, which reached floor 179 after 2,000 generations.

How it works

  1. Game

    The clock runs at a fixed rate

    The game always takes the same size step, whatever the frame rate, so a fast machine and a slow one play the same. If the tab goes to the background the step is capped, otherwise coming back would replay ten seconds at once.

  2. Movement written by hand

    Gravity, friction, and how the player lands on a platform are all written directly. The rule that matters: the faster you are running, the higher you jump. Everything else in the game is built on that one line.

  3. Platforms get reused

    A platform that scrolls off the bottom is moved back to the top instead of being thrown away, so the tower can go on forever without the memory growing.

  4. Three ways to play

    Keyboard, touch, or webcam. In webcam mode your hand steers, and a quick flick up or a pinch makes you jump. The hand position is smoothed first, or every tremor would read as a move.

  5. AI

    The game again, with nothing to look at

    Training needs to play thousands of games a minute, which no browser will do. So the game was rebuilt in Python and run headless: no window, no drawing, just the numbers.

  6. Evolve, do not train

    Population of 200, top 10% carried forward untouched, layer-wise crossover that swaps whole kernel and bias pairs, and a mutation sigma that doubles after five generations without improvement.

  7. One bridge file

    Evolved weights serialise to JSON that both runtimes read, so the network trained in Python is the same one that plays in the browser.

The shape of it

  1. game.js

    the brain of the game

  2. ported to:

    icy_tower.py

    same brain, but headless

  3. 2,000 generations:

    genetic_algorithm.py

    200 pop, 10% elites

    neural_net.py

    the player's decision-maker

  4. writes:

    best_weights.json

    the bridge

  5. read back by:

    Browser AI mode

    same weights, live

The two repos are one system. The Python port exists so training can run without a browser, and best_weights.json is the only thing that crosses between them.

Tech choices

The decisions that shaped it, and why they went that way.

No engine, deliberately
Phaser or Matter.js would have abstracted away exactly the part that was interesting: the momentum-to-jump-height coupling and the collision handling.
Neuroevolution rather than reinforcement learning
No gradients and no RL library. The genetic algorithm is written by hand, which made every design choice something I had to configure.
Taught one skill at a time
Training all of it at once went nowhere, so it was split into stages. Roughly the first 500 generations only rewarded jumping. The next 500 added moving left and right, which is what makes a jump go anywhere. Only then did the score start counting floors climbed. Each stage starts from the winners of the last one, so nothing is relearned from scratch.
Fitness shaped to give early signal
Height times 0.5 plus floor times 1000. Generation zero contains nobody who can land on a platform, so a floor-only score gives every agent zero and evolution has nothing to select on.
One seed per generation
Every agent in a generation plays an identical tower. Otherwise the fittest agent is just the one that got easy platforms, and the run selects for luck.
Layer-wise crossover
Children inherit whole layers from each parent instead of a random mix of individual weights. Splicing mid-layer destroys whatever that layer had learned.

Built with

Game
Vanilla JavaScript (ES6+)HTML5 CanvasWeb Audio API
Input
MediaPipe HandsKeyboardTouch
AI
PythonKeras 3 on JAXNumPypygame
Training UI
FastAPIServer-Sent EventsGradio

What it produces

One run, both halves of the project: fitness and best-floor per generation on the left, the evolved agent playing the browser game on the right. Floor 8 at generation 500, floor 130 by 2,000.

How the repo is laid out

Roughly the order you'd read it in: entry point first, then the parts doing the work.

  • Game

    index.htmlCanvas page; loads the game and hand tracking
  • game.jsPhysics, sprites, combos, coins, ghost replays
  • AI

    icy_tower.pyPython port of the game, headless for training
  • neural_net.pyThe 14-16-8-3 network and weight serialising
  • genetic_algorithm.pyElites, layer-wise crossover, adaptive mutation
  • train_headless.pyThe 2,000-generation curriculum run
  • best_weights.jsonThe bridge: Python writes it, the browser reads it

Where it falls short

The whole game lives in a single 4,400-line game.js file. Splitting it into modules is the top item on its own roadmap and I have not done it.