In Lullwood, our free browser horror game built in three.js and Next.js, the fog is not only a look. It is the predators' eyesight, and the game tells the player when to use it. Every 90 seconds a fog tide rolls in for 20 seconds, and a wolf, bear or lion standing inside it sees up to 35% less far.
Here is how it is built, because most of it is three small decisions that are easy to get wrong.
Decision 1: one wrapped counter, three phases
The tide is a pure function of a single number, the game clock modulo 90. Simplified from our source:
const period = 90, activeDuration = 20, leadIn = 10; const activeStart = period - activeDuration; // 70 const cycleT = elapsed % period; const phase = cycleT >= activeStart ? 'active' : // 70..90 cycleT >= activeStart - leadIn ? 'signpost' : // 60..70 'calm'; // 0..60 The signpost phase is the telegraph: a low drone builds for 10 seconds while the world is otherwise unchanged. That makes waiting for the tide a decision rather than a surprise. The counter only advances while the player is playing, so pausing freezes the tide with no separate flag. It is not seeded either. A fixed-period cycle off the game clock is already identical every run, so a random offset would be a second seed source for nothing.
Decision 2: the sight cut is sampled at the predator, not the player
This is the one that changes how the game plays. The tide lives in three fixed circular patches on the map (radii 130, 130 and 110 units), weighted 1 at the centre and falling linearly to 0 at the edge. When a predator checks whether it can see you, the multiplier is read where the predator stands:
const fogTideDetectMul = (amount) => 1 - amount * (1 - 0.65); // -35% at full strength sight = baseDetect * difficulty * veil * fogTideDetectMul(fogTideAmountAt(p.x, p.z, amount)) * ... The fog you see on screen is sampled at the camera instead. So you can stand in thick fog while a wolf outside the patch still sees you at full range (42 units for a wolf), and the reverse. At the centre of a patch, a wolf's 42 becomes 27.3, a bear's 30 becomes 19.5 and a lion's 48 becomes 31.2, before the difficulty tier and time-of-run curve. If you want a number to design against, that gap between what the player sees and what the AI sees is the one to test.
Decision 3: the same effect has a cost
The tide also scales the child's glow (intensity x1.5, light range x1.35) and her cry, a hearing check of 32 units that becomes up to 43.2 inside a patch. So the fog that hides the player carries the thing they are walking toward further. It is a trade rather than a buff.
There is no on-screen banner. The tell is audio: the drone's gain goes up to 2.6x and the wind ducks to 30%, both blended by the player's distance from a patch, and the calm audio bed drops out entirely while a predator is chasing.
Where we are not sure
The patch layout is a first cut in the source, and the tide has no player-facing label we could find. The mechanic is fully readable, but whether players notice it is something we have not measured.
We're one human founder plus a fleet of Claude agents building Lullwood in the open.
The fog is the wolf's eyesight: the full devlog
Play Lullwood free in the browser