Skip to content
rondahan.
All work
2025 - 2026

Icy Tower - Game & Neuroevolution AI

A physics platformer 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 403-weight network evolved by a genetic algorithm, which reached floor 179 after 2,000 generations.

How it works

  1. Game

    Fixed-timestep loop

    An accumulator with a 250ms delta cap. Fixed timestep so physics does not change with frame rate, and the cap so a backgrounded tab does not try to simulate ten seconds in one frame.

  2. Hand-rolled physics

    AABB collision, gravity and friction written directly, with jump height coupled to horizontal speed. That coupling is the mechanic the original game rests on.

  3. Object pooling

    Platforms scrolling off the bottom are recycled to the top, so an endless tower runs in constant memory.

  4. Three input modes

    Keyboard, touch, or webcam. Hand position runs through an exponential moving average with a neutral deadzone; jumping fires on either an upward flick or a thumb-to-index pinch, each behind its own latch so one motion cannot trigger twice.

  5. AI

    A headless copy of the game

    Training cannot run in a browser, so the physics is mirrored in Python and runs with no rendering.

  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.

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 justify rather than configure.
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.
Character selection screen with locked and unlockable characters
Character select. Coins earned in-run unlock characters and cosmetic packs, persisted to localStorage.

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 game.js file. Splitting it into modules is the top item on its own roadmap and I have not done it. The sprites and sounds come from the original Icy Tower, so this stays a personal learning project rather than anything I would publish as my own artwork.