Painted, not photographed: the ground colour that took two tries to render
The art direction for The Long Watch is one short instruction we keep coming back to: the world should look painted, not photographed. A soft, low-contrast palette, gentle shadows, colour temperature that follows the time of day. The ground is the largest single surface a player ever looks at, so getting its colour right matters more than almost anything. This is the story of how that colour took two tries to render — and how a tiny number some graphics cards quietly round to zero turned an entire biome black in between.
The goal: ground that tells you where you are
The aim was simple to state. There are eight biomes in the world, paired two to a climate across four climates — temperate, boreal, arid, tropical — and each biome was given its own soft base colour. Deliberately painterly: low-saturation, the kind of colour you’d mix on a palette, not the high-contrast colours we use for debug overlays during development. A meadow should read differently from a temperate forest even when the two sit side by side under the same sky. The ground itself should tell you where you are.
On paper the whole job was a short chain. As the world generates, stamp every patch of ground with the identity of the biome that dominates there. In the step that colours the terrain, read that identity back, look up the biome’s colour, and paint. Write, read, look up, paint. We expected an afternoon.

It did not work, and the first read of why was wrong
The colouring step never reliably received the biome identity the generator had written. Worse, the first investigation thought it had proof the data was arriving — there was colour variation across the surface, so surely the signal was getting through. A closer look showed that was an illusion. The variation we’d seen was unrelated data that happened to differ across the ground; the real biome signal was being clamped to a fallback that rendered as black or dark grey right at the world origin, where you spawn and look first.
So began a long, careful chase. We read the source of the meshing library we lean on to understand exactly how it packs the per-surface data the colouring step samples. We ran probe after probe against the actual rendered image rather than trusting what we believed the code did. The finding: the library wanted a second piece of information alongside the biome identity — a per-surface weight — and only when both were supplied would the data survive intact all the way to the point where colour gets chosen.
A valid number that vanishes
The decisive discovery was a hardware quirk, and it is the kind of thing you only find by looking at pixels. For some biomes, the weight value — once packed into the format the GPU interpolates smoothly across a surface — landed as a subnormal floating-point number. A subnormal is a perfectly valid value: just a vanishingly tiny one, down near the floor of what the format can represent. And certain graphics drivers, as an optimisation, silently flush subnormals to zero rather than carry them.
On the integrated GPU we develop against, that flush wiped out the signal for one biome while a neighbour survived. Temperate forest’s weight rounded away to nothing; meadow’s didn’t. The result was a lopsided, broken render: blue meadow appeared as it should, and the green forest right next to it came out black.
The number wasn’t wrong. It was just too small for the hardware to bother keeping.
We tried the obvious mitigations and each failed for its own concrete reason. Flooring the value in the shader so it could never go subnormal. Spreading the weight across more than one slot. Replicating the index. Every one of them ran aground on how the meshing library sorts and packs its data before the GPU ever sees it. The problem lived squarely on the boundary between that library and the graphics pipeline — a seam we don’t own. Nothing in our own world generator or our own shader code could reach it.
Shipping the honest version
When the right look won’t render reliably, the temptation is to keep grinding until it does. We didn’t. We shipped the honest fallback instead: rather than a colour per biome, the terrain was painted with a single colour per climate — the average of all the biome colours in that climate’s roster. Four climates, four distinct averaged tones. The seasonal palette and the time-of-day mood machinery were untouched and kept cycling as before.
The cost was real, and we named it plainly rather than hiding it: inside a temperate climate, meadow and temperate forest now read identically. You lost the ground-level cue that told the two apart. But the world still looked coherent and painterly — the averaged tones are soft and they sit well together — and the climate-averaged look shipped after a single review pass with zero colour tuning. It collapsed the colour-tuning surface from roughly forty-odd knobs down to about a dozen, which is its own kind of honesty: fewer things to get subjectively wrong.
Two decisions made the fallback a stepping stone rather than a dead end. We kept the full per-biome colour data alive in the code, unused, so a later pass could light it back up without re-authoring anything. And we wrote the hardware finding down in plain terms — what the flush was, which biome it killed, why each mitigation failed — so whoever came back to it wouldn’t have to rediscover the whole chase.
The fix: stop fighting the format
About a week and a half later we reopened the deferred question, and the answer turned out to be subtraction, not addition. The blended approach needed those fragile per-surface weights because it was set up to mix several biome colours smoothly across one surface. We switched to a simpler mode that carries only the biome identity and no weights at all — one colour per surface block.
With no weight to pack, there was no subnormal for the GPU to flush. The exact mechanism that had defeated us simply no longer existed in the pipeline. Temperate forest rendered green at the origin instead of black, neighbouring biomes came out cleanly distinct, and there were no black patches anywhere in the world. Per-voxel biome colour was back: meadow and temperate forest now read as different ground inside the same climate, exactly as the original goal wanted.
Two small requirements surfaced and were quietly absorbed. The simpler mode reads the biome identity directly, with no special encoding wrapper around it. And it needs that identity channel stored at a wider depth — eight bits — or the whole terrain renders as one flat colour. Both were one-line accommodations once we knew to make them.
The numbers came out better than the version we abandoned. The render held a steady sixty frames per second — in fact cheaper than the blended approach it replaced, with comfortable headroom above our floor. And all eight biome colours shipped exactly as authored, confirmed in a single review pass with no tuning. To check it in practice we generated a temperate world that split close to evenly between forest and meadow, so both biomes were visible the moment it loaded.
Biomes distinct, zero black, sixty frames per second — and the painted look intact.

What we kept, and what we let go
One thing we consciously set aside: smooth colour blending across biome boundaries. The simpler mode paints in discrete patches, not gradients, and we decided that’s the production look rather than a lingering defect. The world reads as distinct-yet-harmonious blocks of colour, which suits a painted register far better than it suits a photographic one. Soft edges everywhere would have nudged us back toward photoreal, which is the thing the art direction exists to avoid.
Throughout, the discipline held that makes the whole image coherent: the terrain shader only ever sets the ground’s colour, and hands lighting, shadow, and the day’s changing warmth to the engine. The seasonal drift of the palette across the year — the land itself ageing and turning — is a separate story; here the job was only to get the base colour to survive the trip to the screen. When it finally did, the painted palette and the gentle directional light read as one hand-tuned picture rather than a photograph — which was the instruction all along.
The detour cost more than the afternoon we’d budgeted, and that’s the honest shape of it. But the lesson travelled. Ship the coherent first-pass colour over the perfect one; name the loss out loud instead of papering over it; keep the data you can’t use yet alive for the day you can; and when the hardware refuses to carry your number, find the path that never asks it to.



