Glimtide: A Living Island in the Browser That Never Stops

My new browser game: one shared island of jelly creatures who think, learn and evolve on their own, and an ECS world that takes new mechanics without a restart.
There is one island in the ocean of Tidemere, and right now a few hundred small jelly creatures are living on it. Some are asleep in their nests. Some are arguing over a mango tree. One of them is wading into the shallows to catch a fish, and there is a shark fin a few tiles away that it has not noticed yet.
That island is Glimtide, my new browser game. You can open it at glimtide.io and watch the same world everyone else is watching, at the same moment. Close the tab and it keeps going without you.
This post is about what the game is, how the creatures think, and the one technical idea the whole thing is built around: new mechanics go into the live world while it runs, without a restart.

What you actually do
Very little, on purpose. Glimtide is closer to an aquarium than to a strategy game. Nobody controls the glims: they decide for themselves what to do next. You watch, and now and then you nudge.
- Food. Drop a pile of treats once a minute. Glims who overeat get "Overweight" in their card and slow down for a while.
- Play. Poke a glim. It cheers up, and every need refills a little, which is a nice excuse to come back to a glim you like.
- Adopt. Sign in, take a free glim under your care and give it a name. Up to three per account. You get a notification when it learns a skill, finds a partner, has a baby or gets chased by a predator.
- Hazards. Small, rate-limited pranks: a banana peel to slip on, a screaming critter, a predator. Only the predator can actually kill.
- Your own species. Design a kind of glim (colour, pattern, eyes, crest, home biome and ten points of traits) and it lands on the island as a tribe of five, as far from other nests as possible. Then it is on its own.
Every one of those actions also fills the Shared Tide, a meter all viewers fill together. At 120 points the sea brings a tide, and it picks the one the island needs most right now: abundance when the glims are hungry, a quiet night when predators are about, a tide of insight that speeds up learning, a shimmering tide that makes mutations three times more likely. One tide even raises a 5×5 patch of a different biome out of the sea, with its own plants.

A glim's life
A glim lives for about one hour of real time. That one number drives everything else: a game day is a slice of it, hunger and fatigue grow at rates derived from it, and you can change it from the admin panel on the live world. At an hour per life you get dozens of generations a day, so evolution is something you can actually watch over an evening.
Each glim has needs (rest, food, love, mood) and a life cycle: a baby follows its mother everywhere, a youngster copies everyone around, an adult finds a partner and raises children, an elder slows down. They die of old age, hunger, predators, sharks or disease. When one dies, its ashes stay on the spot for about half an hour of real time. Family members come to them from across the island, passers-by sometimes stop to grieve, and the relatives walk around sad for a while afterwards.
How a glim decides what to do
The brain is utility AI, not a neural network and not an LLM. Every few ticks a glim scores every action available to it with a number and takes the best one. An action is a small object any module can register:
const nap: ActionDef = {
id: 'siesta_nap',
kinds: ['glim'],
score(e, w) {
const n = w.get<Needs>(e, NEEDS)
if (!n || n.asleep || n.hunger > 0.6 || n.fear > 0.3) return 0
if (!inSiesta(timeOfDay(w)) || n.fatigue < cfg.tiredness) return 0
return 0.35 + n.fatigue * 0.4
},
start(e, w) { stop(w, e); w.add(e, NAPPING, {}) },
tick(e, w) { /* rest a little; return 'done' or 'running' */ },
stop(e, w) { w.remove(e, NAPPING) },
thought: () => 'The sun is warm... just a little nap...',
}
Two small details make it look alive rather than mechanical. The current action gets a bonus of 0.12, so a glim does not flicker between two almost equal choices. And there is a little noise from the world's random generator, so two identical glims in the same spot do not move in lockstep.
Personalities plug in from the side. A glim is calm, lazy, responsible, a Lovelace or an Untouchable, and each one is just a set of multipliers on the scores of certain actions. Lazy glims sleep a lot and wander less, a Lovelace courts even when hungry or tired, an Untouchable rarely pairs up but heals faster. Children usually inherit a parent's personality.
Families, skills and species
This is the part I find most fun to watch.
Everything a glim carries is a mutation: skills (swimming, climbing, foraging, sprinting, building), the knowledge of which foods are safe, and rare inborn traits. They spread in four ways:
- Inheritance. A child gets each of its parents' mutations with a good chance.
- Practice. Swimming near water teaches swimming. Skills have levels from 1 to 5 and grow while they are used, so a master builder builds twice as fast.
- Conversation. Glims stand next to each other and chat (you see the speech bubbles and hear the chatter). Each conversation gives the listener experience in the speaker's skills and foods. Babies also learn by watching their parents.
- Birth. Now and then a newborn has something no parent had.
Food is the clever one. Every food is its own mutation, and the first glims only know what grows where they landed. A beach tribe knows coconuts and grain; it has to meet a jungle tribe, or starve hard enough to try a banana, before bananas become food. The rare Traveler mutation sends a glim on long journeys to far-away tribes, and travellers are how knowledge crosses the island.
The rule I hold every trait to: nothing is useful everywhere. A fur coat helps in the tundra and is miserable in the desert. The rare Sleepyhead mutation recovers slower in sleep but barely gets hungry while asleep. Climate tolerance is learned by living somewhere, is inherited, and cannot be picked up by chatting.
When a child ends up too far from every living species, a new species begins, with its own generated name and colour. Species pass down the father's line. The encyclopedia on the site lists every living species with live counts, and the chronicle announces each new one.

The island
The island is 320 by 320 tiles and is generated once from a seed: height and moisture noise give the biomes, and the land is split into climate belts from south to north by area, so every island gets all of them: desert, plains, jungle around the equator, plains again and tundra in the north. Each biome grows its own plants. Rich food grows slowly or somewhere hard to reach: snow berries grow only on the peaks, and only glims who can climb ever taste them.
The first glims land as small tribes of three to six, far apart. A tribe starts out looking identical (colour, pattern, eyes and crest), but their genes differ, so they drift apart from the first generation.

There is no population cap anywhere. The population is held in check by food, by predators that keep to their own climate belt, by sharks that quietly take glims who paddle too far out, and by disease. Disease is the one mechanic that is not built in: I install it into the live world from the admin panel, and it is my standing test that hot modules really work.

Who tells the story
An island with a few hundred autonomous creatures is mostly noise. Two systems turn it into something you can follow.
The director watches the event stream and picks the best moment to show: a shark attack, a traveller arriving, a glim getting better at a skill. In "Best moments" mode the camera flies there, the hero stands in a golden ring, and a caption says what is happening, why this moment was picked and how interesting it is.

The chronicle writes the island's history: births and deaths, new species, tides, sharks. Important lines are then retold by a narrator service that calls Claude, in English and Russian. That is the only place a language model touches the game. It retells the chronicle and changes nothing. Every decision on the island is made by fast, deterministic module code, and if the narrator is down or out of budget the game carries on with the plain chronicle.
The technical idea: a world you never restart
Here is the requirement I started from: I want to keep adding mechanics (houses, storage, disease, hunting) to a world that is already running, without stopping it, and without losing a single glim.
Everything is a module
The core of the server knows almost nothing about glims. It stores data and calls systems in order. Everything else, even hunger, is a module:
export default defineModule({
id: 'siesta',
version: '1.0.0',
title: 'Siesta',
description: 'Tired glims take a short nap in the afternoon.',
requires: { core: '^1', needs: '^1' },
components: { Napping: { version: 1 } },
config: { from: { type: 'number', default: 0.5, min: 0, max: 1, label: 'Siesta starts' } },
setup(ctx) {
ctx.extend(ACTIONS_REGISTRY, nap.id, nap)
ctx.extend(VIEW_DECORATORS, 'siesta', (w, e, v) => {
if (w.has(e, 'Napping')) (v.tg ??= []).push('zz')
})
},
})
Twenty modules are installed into every new island: core, genetics, flora, needs, lifecycle, skills, climate, reproduction, character, mourning, herbivores, predators, sea, viewers, creator, hazards, tide, director, chronicle and a watchdog. They never import each other's internals. They talk through registries (ctx.extend(...): brain actions, viewer commands, genes, skills, food filters, tide events), through events (creature.born, skill.learned, shark.attack…) and through modifiers. That is why a new module shows up in the chronicle and the director on day one: they listen to events, not to modules.
The config block is not decoration either. The admin panel builds its forms from it, and a change applies to the live world on the next tick.
Installing into a running world
Shipping a new mechanic looks like this:
- I write the module and push a tag like
module/disease@1.0.1. - CI runs the tests and a deterministic simulation run, builds the module into a single file and uploads it.
- In the admin panel I press Trial run. The server takes a snapshot, starts two copies of the world in a separate worker, one with the module and one without, and compares them.
- Install attaches the module between two ticks, with a snapshot taken first and data migrations if a newer version changes its data format.
Two rules make this safe. Modules are deterministic: randomness only from the world's seeded generator, time only from ticks, never Math.random() or Date.now(). Otherwise the trial run compares noise with noise. And there is a circuit breaker: a system that throws five times within a hundred ticks switches off only its own module, the world carries on, and I get an alert in Telegram.
Viewers never see a restart
Sometimes the core itself has to be updated, and that does need a restart. So viewers do not connect to the simulation at all. They connect to a gateway that keeps its own copy of the world view. When the simulation goes down for an update, the gateway keeps everyone connected and shows "The world is dozing…". The world saves a snapshot, restarts, loads it and carries on from the same tick.
| Service | What it does |
|---|---|
sim | The world: 10 ticks a second, modules, snapshots, trial runs in a worker |
gateway | WebSockets for viewers and the admin panel, sends each viewer only what is near their camera |
api | Sign-in (Google or email), history, epochs, pet memorial, admin REST |
narrator | Retells the chronicle with Claude. Optional |
client | The site: Vue 3 and PixiJS, English and Russian |
admin | The admin panel: modules, settings, director, inspector, alerts |
All of it runs on one Hetzner server under Docker Compose, behind Cloudflare. The world saves a snapshot to disk every 5 minutes and to Cloudflare R2 every 30. The database is dumped every 6 hours. If the server dies, a new one is up from the last snapshot without losing more than a few minutes of the island.
And if the glims die out completely, that is not a bug either. A new epoch begins: a new island rises from the sea, with new glims that know only the food of their own shore.
The numbers
| Language | TypeScript everywhere, on Bun |
| Code | about 47,000 lines in apps, packages and modules |
| World modules | 20 built in, plus disease installed by hand |
| Simulation | 10 ticks a second |
| Island | 320 × 320 tiles, 7 biomes, 5 climate belts |
| A glim's life | about 1 hour of real time |
| Languages | English and Russian on the site, Russian in the admin panel |
Claude Code worked across the codebase with me, and the game's design documents live in the repo next to the code, so it always knows which decisions are already made.
What is next
The design documents have a long list. Hunting, so herbivores turn from competitors into food. Houses and storage. More tides and more pranks for viewers. Each of them is a module, so each one can go into the island you are watching without it ever closing.
Go and say hello to the glims at glimtide.io. The game page with the press kit is here, and if you want the ECS part in short form, I wrote it up as a TIL note.


