Role
every hat
- Game design
- Interface
- Art direction
- Code
Case study · Ice Age Life
A life sim of the last Ice Age that I designed, built and shipped alone. Be born into a band, live one life to its end, then start over as someone new.



Every life in it ends. That is the game.
The hard part was a world deep enough to surprise me that a stranger could still get through their first spring.
Role
Timeline
6,678 commits since March 23, and 12 native releases since June
Platforms
One Expo and React Native codebase, TypeScript end to end.
Scale
You live one prehistoric life, birth to death, a year at a time. When it ends, you start again as someone new.
A simulation deep enough to surprise its maker, that a new player can still get through their first spring.
A season board you can read at a glance, a world that keeps itself alive, and art and words held to strict rules.
The audience is small: 22 devices reached their first season in October. So this is design reasoning, simulations and what that small group showed me, not a growth chart.
One whole life
You’re born into a band of Ice Age foragers, 22,000 years ago, and live one life a year at a time: childhood, a name at 18, a hearth, children, old age. When you die, the run is over, and your soul starts again as someone else.

Initiated
When the bands meet, you're offered names, shaped by what you've done.
Each year brings a life card to answer and a season board to play.
There's no save to reload. The life is over, and the next one starts as someone new.
Everyone else is living too
Nobody in the band is scenery. They pair up, have children, grieve, fall out and move away on their own schedules, and each of them has a mood and a feeling about you.



A band can grow into something bigger: proto-tribe, big man, paramount, chief. The game stops at early chiefdom on purpose. Past that, you're no longer living one person's life.
The Ice Age is live across eight homelands, from the Mammoth Steppe to a glacier coast at the world's edge. Later eras wait behind a lock.

Dnieper Mammoth Steppe

The East Wind Plains

The Great South Land

The Frozen Heart

The Painted South

The Sheltered Valley

The Great Rift

The World's Edge
The season board
Each year opens on what the season brought: threats like wolves or a mudslide, bounties like the first greens. You decide who meets which. Send the wrong person at a wolf pack and you lose them. Send nobody and everyone goes hungry. Try a round.
Each front says whether it holds before you commit, and counts harm that reaches past its lane, like a threat that strikes camp.
Every result prints its arithmetic, like Hunt 53 +21 lent = 74 vs 50. A loss you can check is one you learn from.

Where new players quit
Play data showed newcomers quitting inside the very first board. The birth year ran about eight rounds because the whole band had to play, Auto-play stayed grayed out all year, and the board could dead-end on “Fill 1 more column” with nobody left in your hand.
22 devices reached their first season
4 got past it
October 2026, from the game’s own analytics. A small number, and enough to see a wall.
The dead end: an empty hand, and a button asking for one more column.
| Birth year | Before | After |
|---|---|---|
| Who places the band | you, everyone, by hand | you for two rounds, then the band |
| Rounds in the birth year | about 8 | 2 |
| Auto-play | grayed out all year | on the main button from round 1 |
| A lane nobody can work | a dead end | settles itself |
Two thirds of first lives were ending before age 5. A learning life now shelters you: until you've lived to 5 once, the board never seats your own baby, and a death on a lane becomes a wound you pull through.
Auto-play from round 2, a button that moves someone into the empty column, and an analytics event for every round, so the next look would have numbers.
The numbers came back: 4 of 22 got through. Nobody tapped Auto-play before round 8, though it had been there since round 2.
The redesign, out the same evening. A first life's birth year is two rounds, the first by hand and the second with Auto-play on the main button. Then the band finishes the season on its own.
A band that stays alive
A life sim needs a band that’s still there when you’re old. Measured against real forager life tables, mine wasn’t: run 20 bands for 60 years in the loop the game actually plays, and every one fell below ten people. Step through two days of fixes.
The yearly roll was a full forager table with the board's dangers on top. It now keeps only what the board doesn't cover.

Past about 35 people on the Mammoth Steppe, a band sends the household least tied to it to a neighboring band. Never yours, a leader's, or a pregnant one.
Runs vary hugely from seed to seed, so every change is judged on 20 to 40 seeds over 60 to 100 years, against a recorded baseline.
1,175 icons, one hand
The store page says it’s all words, and it is. But every tile, front, person and choice also carries a drawing, so a crowded screen has a shape before you read it. Filter the set.
11 looks × 16 temperaments × 2 × 5 ages = 1,760 faces
Each choice on a card gets its own icon for what it does: none shared on a card, no gray placeholder. A lone Continue is a walking figure. A test fails if one is missing.
Emoji draw differently on every phone and break the hand-made look. The game only speaks in its own set.
Words that state the rule
Ice Age Life is mostly words, so the words get rules too. Flavor keeps its voice. A sentence that teaches you something drops the mood and says it plainly.
While you’re still small, you place the band by hand - that is how the seasons teach you. Auto-fill opens once you’re old enough to help set them.
Auto-fill unlocks at age 4. Until then you place the band by hand.
Modern, plain and casual, like a person talking, not a narrator. Words that sound like a costume get cut.
The listing still said “Free, and no ads” after a version with ads had shipped. I found it in July and rewrote it.
A color for every day
Each of the 366 days has its own pair of palettes, one for light mode and one for dark. Seasons, holidays and animals each get a day: Halloween is a pumpkin patch by day and All Hallows’ Eve by night.
Light · newyeardawn
Dark · firstdawn
One codebase, three stores
It’s one Expo and React Native app in a TypeScript monorepo with shared types. The web version is the same game, exported and served at iceagelife.cc, and one capabilities file decides what each platform gets: on the web there are no in-game ads, purchases or accounts, just a banner under the game.



A run never needs a server. The save lives on the device; an optional account only backs it up, one snapshot, never merged.
The app opens straight into play. Backup is offered after your first finished life, not before your first tap.
The web deploy loads the live site afterwards and fails if the game isn't being served in its frame.
Over-the-air updates are stamped with the app’s version at the moment they’re published, and only builds of that exact version take them. Try both orders.
Tests that can fail
About 13,000 tests run on the game. The rules I trust most came from green suites that shipped bugs: ten homeland cards passed every test and never fired once, because the tests built their runs by hand instead of the way the game does.
jest
.spyOn(Math, "random")
.mockReturnValue(0.99);
// never: Math.random = () => 0.99;A raw assignment outlives its test and pushes later suites down paths nobody chose. A test fails the suite if anyone writes one.
const rng = createRng(
deriveSeed(runId + "dogs", turn, 0),
);A new system draws from its own stream, so switching it on doesn't reshuffle every other system's dice.
A regression test is unproven until it fails against the original bug, with a control case beside it.
Face file names once disagreed with 80 shipped files on the order of two words, and a whole look rendered as a stand-in. Now every name has a file, and every file a name.
A seeded sweep plays 5,000 boards and checks that every stuck hand has a move that clears it.
What I learned

Ice Age Life
Free in any browser with nothing to install, and on iPhone, iPad and Android.