2026-09-04 · ALIVE · BUILD 1.2

The pixel grid was the mechanism

Character art was rejected twice before it landed, and the second rejection was caused by a bad brief. The fix turned out to be a fixed pixel grid, which also doubled the frame rate. Then a crowd that gated its own corridors, and a tech tree nobody would ever reach the end of.

ALIVE is a simulation you cannot play: a hundred people wake in a world that has never been walked on, and you watch. Two updates today, from 1.0 to 1.2.

Two rejected art builds, and why the second was the brief's fault

The reference was four Stardew Valley screenshots and one from CrossCode. The first attempt at matching them was written up as a reference document, and section 3 of it said to copy Stardew's faces literally: two-by-two dark blocks for eyes, no nose, no mouth.

The build followed it exactly, and the verdict was that the people had lost their eyes. That was right, and the brief was wrong. Stardew can strip a face to two dark pixels because its camera never moves. ALIVE's camera zooms to 3x, follows individual people, and lets you click someone and read their thoughts. These faces get looked at. The section now opens by saying it was wrong.

The part of the reference worth taking was never simplification. In the Flower Dance screenshot, two full rows of characters wear identical white dresses and identical blue shirts, and every one is instantly distinguishable across the crowd. The whole mechanism is hair shape and hair colour. That matters here because up to 260 people can be on screen, and it costs nothing in the face.

Six candidates, side by side

The process changed too. Each candidate was built in its own file and registered into a variants table without being allowed to make itself active. A comparison page drew them as rows against the current art: same nine people, same states, same light. Every candidate had a checker whose first job was to confirm the eyes, iris, catchlight, brows and mouth were all still there at full size.

rowwhat it changedoutcome
Anothing, the baselinereference
Bcolour and light only, not one coordinate movednot chosen
Cbolder build, distinct hair, same facenot chosen
C2pixel grid at 3.25 px per world unit (~54px tall)not chosen
C3pixel grid at 2.6 px per world unit (~43px tall)not chosen
Dpixel grid at 2.0 px per world unit (~33px tall)chosen, shipped

Chunkier pixels read more like the references and hold up better over a crowd. The face detail lost at that size is a real cost, accepted deliberately with the alternatives on screen.

The pixel grid is the mechanism, not a preference

Both reference games are pixel art. Every edge lands on a pixel boundary, there is no anti-aliasing, and the pixel is the same size everywhere in the frame. ALIVE drew anti-aliased vectors at a continuous camera zoom, so every edge was soft and the effective pixel size changed as you zoomed. No amount of better shape-drawing could close that gap, which is why two attempts at better shapes both failed.

  • Draw once into an offscreen cell at a fixed 2.0 pixels per world unit.
  • Blit back with smoothing off, at an integer magnification, to a whole-pixel destination.
  • Absorb leftover fractional zoom by re-rasterising at a slightly different density, quantised to 1/32. Never scale the blit, because a fractional scale puts the soft edge straight back.
  • Quantise poses to whole frames like a real sprite sheet: 8 for people, 10 for beasts.

It is also twice as fast, which was not the point but is the best argument. Measured back to back in one session at 200 souls, swapping only the draw function:

buildframe rate at 200 souls
old vector art, drawn direct13 fps
pixel build, rasterised and cached26 fps

A cached blit is one drawImage instead of about fifty canvas operations. Rasterise-and-cache pays for itself.

The same pipeline now wraps the monsters, one atlas per creature kind. The cell is sized from the creature, and getting that wrong the first time cropped the tyrant to a featureless grey slab.

Bugs with a lesson in them

Every villager was wearing a bandit mask

Four places did the same thing: fill the shape, save(), clip(), run a shading pass, restore(), then a bare stroke() for the ink outline. By the time that stroke ran, the beginPath() calls inside the shading block had destroyed the path, so the "outline" was stroking the last small ellipse drawn inside the clip. On the head that was the pair of under-brow shadows, which at outline weight merged into a solid dark band straight across the eyes. On the tunic it inked the fold lines, which is where the plaid look came from.

It was found by instrumenting rather than guessing: a proxy canvas context that samples probe pixels after every paint operation and reports which one changed them. Operation 46, a stroke, took both cheeks from 180,128,95 to 48,35,34 in one call. The fix is to build each shape once into a Path2D and pass that same object to fill, clip and stroke.

Monsters strobed walking north or south

Facing was Math.cos(dir) >= 0. A creature heading almost due north or south has a horizontal component near zero, so tiny jitter flipped the whole sprite every frame. Facing is now held on the creature, changes only when the heading is decisively the other way, and takes six frames to commit.

Every tree had the same hash

Tile vegetation is baked with x and y passed as zero and the tile position applied as a translate, so every tree called the hash with the same inputs and got the same value. That is a large part of why the foliage read as flat. Tile identity now comes from the canvas transform.

Light on water, and clouds that move at the right speed

The brief for the sea was that it should respond to the sun and moon dial in the corner the way the rest of the world does. A glint appears where a wave face happens to be tilted to bounce the light straight at the camera, so the test per ripple is how well its slope lines up with the incoming light. The whole glitter field flips as the sun crosses the sky, spreads and reddens toward dusk, and hands over to a narrow cold silver track when the moon takes over. Underneath: three depth bands, two crossing swells (one wave train reads as corrugated iron, two read as sea), shore surf, and caustics in the shallows.

A second treatment was built behind a flag, compared, and removed, because it hazed the blue toward grey at low sun.

The camera looks straight down, so a cloud is nearer the lens than the ground and must pan faster than the land beneath it. Cirrus moves at +3%, cumulus at +10.5%, low scud at +19%. That difference is the whole illusion of height.

Armour, and the weapons that answer it

Monsters got roughly three times the health and an armour field. Every blow resolves as max(damage * 0.15, damage - armour). That one line is what makes the technology ladder matter: a flint arrow doing 8 to 16 against a tyrant lizard's 18 armour does almost nothing, while a laser lance doing 70 to 125 tears it open.

Projectiles are real objects with a lead, a spread that narrows with the shooter's skill, and travel time, not hit rolls. Whatever speed the game is running at, a boss fight drops to 1x and holds there while the thing is among the people, with a badge saying why. Measured balance: 99 stone-age villagers swarming a tyrant take it from 2900 to 1930 in 25 seconds and lose three of their own. A fight, not a mob execution.

The violence audio (screams, yells, weapon and boss voices) was built and then removed. The call was to keep the game ambient and soothing: ambience and music tones only.

Personal space was gating the map

The previous build stopped people walking through one another. It also introduced a failure nobody had looked for: they piled up and plugged passageways.

The cause is specific. Two people meeting head-on in a corridor are each pushed sideways, into the walls. The separation code refused any push into unwalkable ground, so neither moved, the pair never resolved, and the corridor stayed plugged for as long as both wanted it. Over six worlds, somebody spent up to 36.9 seconds getting nowhere.

Three fixes, each aimed at a different half of the deadlock:

  • A push into a wall now slides along it. The move is decomposed per axis, so someone shoved north into rock in an east-west corridor still travels east.
  • Personal space is no longer a constant. Anyone who keeps failing to make ground gives it up, down to almost nothing over about two seconds, so a queue dissolves rather than gating the map. It returns the moment they move again. This is the guarantee that no jam can be permanent, and it does not depend on having predicted every way a crowd can wedge itself.
  • Whoever is going somewhere keeps their ground; whoever is standing about yields it, 0.28 against 0.72, normalised. The people plugging a bridge are generally the ones not on their way anywhere.

Over six worlds, 220 souls, 90 seconds each:

v1.1v1.2separation off
ticks spent stalled while walking7.7%4.6%4.4%
people stuck 4s or more4710
worst single stall36.9s8.7s0.4s
arrivals (paths completed)16,79418,41320,132
overlapping pairs (within 3px)2.31.3742

The control column is the point. With separation switched off entirely the world still stalls 4.4% of the time, so what is left in v1.2 is the world's own traffic and not the crowd. Overlap did not regress to buy the improvement.

Five world sizes

World size is now a setup choice: Cramped 86x60, Standard 118x82 (what the game has always been), Large 156x108, Huge 204x142 and Vast 260x180. That is 0.53x to 4.84x the old area.

The generator works in tile units, so a bigger map is not a magnified one. Hills, groves, coasts and rivers keep their size and there is simply more land, which is what makes a big map worth crossing. Resources come from the same per-tile rolls and scale on their own.

Vastcountagainst Standard
land tiles19,6054.50x
tree tiles4,5884.49x
stone8824.50x
berry2,4944.07x
fish4,3745.49x
critters1734.55x

Two things do not scale by themselves. Rivers are counted per world rather than per tile, so they scale by area^0.85 or a big map comes out dry. Population ceiling and monster count scale by the square root of area, because four times the land does not want four times the crowd. Vast tops out at 572 people.

A fifth world shape, Wildlands, starts from one of the four named shapes and moves every dial off it from the seed. Across four seeds it produced land shares from 22% to 30%: a different country each time rather than the same continent under new noise.

One thing had to give. Terrain is baked into a single offscreen canvas, and iOS refuses anything over about 16.7M pixels. The supersample is now spent against a fixed 12M pixel budget: Standard and Cramped keep the full 2x, Large gets 1.67x, Huge 1.27x, Vast 1.00x. The largest maps trade terrain sharpness for existing at all. Measured live in the browser, Huge draws in 3.2ms median and Vast in 2.3ms; generation takes 843ms at Vast.

Nobody was going to see the later ages

The brief was that people should start advancing technology every year, slowly, and that a people who last a long time should advance greatly. Before touching anything, twelve in-game years were measured across three worlds.

yearpopulationage reachedresearch per hour
177Kindling0.33
470Farming0.22
834Masonry0.39
124Masonry0.09

Twelve years to reach the second of ten ages, with a population falling from 100 to 4. The last age costs 4,100 research points; at the observed 0.3 an hour that is a hundred and forty years for a people that dies in twelve. The later half of the tech tree was decorative.

Research is now two terms. The first is a drip sized against the cost of the age in front of them: one age per 5.5 years whatever the age. Nothing multiplies it, not famine, not war, not losing every scholar, so a people that merely survives gets there. The second is effort, and it now includes what they are hauling home, with a bonus of up to 1.85x for out-gathering the other peoples in the world.

same world, twelve yearsbeforeafter
age at year 4FarmingWriting
age at year 8MasonryEngineering
age at year 11MasonryIndustry

What was actually killing them

The collapse above was assumed to be famine. Instrumenting every death over eight years gave 109 deaths: 57 to a devourer's beam, 33 to wounds, 10 to a raptor pack, 2 to a bear, 1 to a great wolf. Two to cold, one in childbirth, three of old age. Not one to hunger.

And the defences did not work in a more basic way than being weak. The defence value on a wall, a keep and a castle was read by nothing at all. It was a number in a table. Defences were also only built while at war with another people, so a town could be eaten by raptors for forty years without putting up a single stake.

Eleven new works now run from sharpened stakes and a watch fire in the first age to a turret in the last. The ones that shoot find the nearest thing that wants their people dead and fire through the same ranged code the archers use, so a ballista bolt sticks in a tyrant exactly the way an arrow does. A tower has no skill, fear or morale, so it never breaks off, and it cannot hit anything in the air unless it was built to, which is the reason a people ever builds a flak tower.

They build them because something is eating them. A per-people measure called menace rises when something puts a claw into one of them, rises further during war, and decays, so a quiet century lets them go back to building libraries. Above a threshold one build in three goes to the walls; above 0.75 they drop everything else. Each rung is the best thing they know how to build that they do not have enough of yet, so a stone-age band puts up stakes and a modern city puts up turrets off the same rule.

Walls are expensive to cross, not impossible. Making them solid would let a people wall itself in and starve behind its own defences, and would put back exactly the passage-gating the first half of this post was spent removing.

Measured over three eight-year worlds, they build watch fires, ditches, watchtowers, gatehouses and archer towers unprompted, and the towers fired 18, 5 and 31 times. The structure count went from 44 to 55 and the trade count from 21 to 25.

Smaller things

The minimap was a flat 176 pixels wide, a fifth of a laptop window. It now takes a tenth of the viewport between sane limits. The size lived in two places, the drawing and the click test, which is how a minimap ends up with a hit target that does not line up with the picture. There is one definition now.

The fallback building renderer had no default case, so any hand-drawn structure without an explicit branch drew nothing at all. Eleven new defences would have been invisible in that variant.

One honest note on balance: the defences are built, they fire, and they help, but a world at high monster settings still grinds a hundred people down to a handful inside a decade. Whether that is the game being hard or the game being broken has not been decided, and it should be decided with numbers rather than a feeling.