The Living World

A forest that grows itself.

Plants in FloraForge are not placed and frozen. Every one is born, ages through four stages, and — once mature — casts seedlings of its own before it dies. A single world clock drives the whole simulation: growth, death and despawn are all computed from a plant's age, and one reproduction round runs each sim-day. Populations climb toward a full state, then keep turning over: old trees fall, gaps open, seedlings race in.

4 life stages Growth tick: 1 sim-hour Spread pass: every sim-day Forests turn over

Four ages of a plant

Every plant carries a GrowthStageSeedling, Young, Mature, or Dead — and nothing more about its age. The stage sets how it draws: a seedling renders at 0.15× its species' full size, a young plant at 0.50×, a mature plant at full scale, and a dead one becomes a leafless grey-brown snag at 0.85× — crooked, slightly leaning, easing smaller as it rots. The same oak recipe expands into a sapling, a spreading giant, or a weathered skeleton depending only on which stage it has reached.

SEEDLING 0.15× · from hour 0 YOUNG 0.50× · 48 sim-hours MATURE 1.0× · 144 sim-hours DEAD 0.85× → 0.68× · leafless snag GONE ground freed 0 h 48 h 144 h ~864 h ±25% +~120 h AGE IN SIM-HOURS →

The default schedule: seedling for 48 sim-hours, young for a further 96, and mature at 144 sim-hours — roughly six sim-days from sprout to full size. A tree then stands mature for its lifespan_hours (720 by default, jittered ±25% per plant so a generation never dies in one wave), spends snag_hours (120) as a standing snag, and despawns. Each species carries its own timings — shrubs live 360 hours and rot in 48, while the base-only cattail is immortal (it cannot respawn through the spread pass, so death would empty every reed bed for good).

The clock that drives it

The whole life cycle is analytic. A plant never accumulates a timer — it stores only the sim-hour it was born, born_hour, and its stage is a pure function of its age. Subtract birth from the world's total_hours and compare against the species' thresholds; that one calculation yields seedling, young, mature, dead, or gone with no per-tick bookkeeping. The death and despawn thresholds are jittered per plant from a hash of its identity, and base-world plants draw a uniform fraction of a full lifespan — so the starting forest dies as a steady trickle, never a synchronized wave.

Growth tick 1 sim-hour

Cheap, off the hot path

  1. Skip the settled.Each chunk records the next sim-hour any of its plants crosses a threshold; a chunk whose hour hasn't come is skipped entirely.
  2. Recompute stages.For the rest, the stage — including death — is re-derived from age; no state to advance, just comparisons.
  3. Reap the gone.Plants past their despawn hour are removed, freeing their ground and waking the surrounding chunks for spread.
  4. Refresh on change.If anything changed, loaded chunks are told to re-draw at the new scale.

Spread pass 24 sim-hours

One reproduction round per sim-day

  1. Emit.Every mature plant may cast a seedling candidate near itself.
  2. Validate.Each candidate is point-tested against terrain, the species' rules, spacing, and house clearance.
  3. Append.Survivors are born at the current hour and start their own life cycle.
Because the stage is derived, not stored as a running counter, a world can sit idle or fast-forward its clock and every plant still resolves to exactly the right age — no catch-up loop, no drift. (Reproduction is the one exception: a birth is an event, not a derived state, so a leaping clock replays missed spread rounds — see below.) The same trick makes death free to persist: a despawn needs no tombstone in the save, because a reloaded world re-derives the same death day from the same identity and simply never materializes the plants whose time has passed. Persistence only needs the clock's total_hours and each plant's born_hour to reconstruct the whole living — and dying — world.

The daily spread pass

Once a sim-day, mature plants reproduce. The pass is deliberately two-phase so it can run in parallel without two seedlings ever racing for the same patch of ground. Phase one only reads; phase two only writes, one chunk at a time.

source chunk neighbour chunk · 256 m river margin mature · emits spread radius ≈ 25 m accepted · valid ground rejected · landed in water

Phase 1 (read-only, parallel): each mature plant rolls its spread_chance (default 0.3) and, if it passes, throws one or two candidate seedlings within its spread_radius (default 25 m — close by, but far enough to cross a chunk border, so a seedling lands in the parent's own chunk or a direct neighbour). Phase 2 validates every candidate against the target chunk's height, moisture, slope, rivers and sea level, the species' placement rules, spacing against existing plants, and a keep-clear ring around houses (10 m for trees, 5 m for shrubs) — then appends the survivors.

Spacing is the capacity gate. A candidate is rejected if it lands too close to a plant already there (8 m for trees, 3 m for shrubs). As a patch fills, fewer and fewer candidates find open ground, so each chunk climbs toward a bounded full population and then holds steady — the forest reaches a natural carrying capacity rather than growing without limit.
A fast clock doesn't starve the forest. The spread roll is salted with the sim-day, so every mature plant draws a fresh, independent chance each day — failing today never means failing forever. And when a high day_speed makes one frame span several sim-days, the runtime replays the missed reproduction rounds (up to eight per frame) instead of collapsing them into one. Births stay proportional to sim time, not frame rate — without this, the analytic death rate would outrun a frame-throttled birth rate, and every mortal species would slowly thin toward extinction.

Why the forest stops growing

A simulation that touched every plant every sim-day would get more expensive as the world filled up — exactly backwards. FloraForge inverts that with saturation tracking: a chunk whose last spread pass added nothing, and which holds no immature plants, is flagged saturated. There is no open ground left and nothing still growing into a spreader, so it has nothing to contribute.

Because a plant can only spread into its own chunk or one of its eight neighbours, the pass skips a chunk only when it and all eight neighbours are saturated — a packed chunk still feeds a half-empty one across the border. Once a whole region fills in, that entire neighbourhood is skipped, so the simulation's cost falls as the world matures. A maturing seedling next door instantly clears the flag and reopens the chunk for reproduction.

Death is what keeps a saturated forest from freezing forever. A standing snag still holds its ground — seedlings can't sprout inside a dead trunk — but the moment it despawns, its chunk and the eight neighbours are un-saturated and the next spread pass competes for the gap. The forest settles into a dynamic equilibrium: a steady trickle of deaths opening ground, and a daily race of seedlings closing it again.

Cattails are the exception that proves the rule. The aquatic reed has spread_radius 0 and spread_chance 0 — it never joins the spread pass. Instead it is seated once, at world creation, in the wet river and lake margins that land plants avoid: its aquatic flag inverts the usual wetness guard, so it grows exactly where everything else is rejected.

Saving only what changed

The base world — every plant placed at creation — is fully regenerable from the world seed, so it never needs to be written to disk. A running game only changes that world by spreading new seedlings on top of it.

So the save is just the spread-delta: the suffix of new plants each chunk has grown beyond its deterministic base, plus the clock's total_hours. Reload regenerates the base from the seed, replays the saved seedlings, and the analytic growth model resolves every plant's stage from its born_hour — the whole living world reconstructed from a compact diff.

From growth to evolution.

Growth and spread are the foundation. The next step turns a species' fixed recipe into heritable traits — letting populations adapt to the terrain they grow on. See where the simulation is headed, or return to the field guide.

Evolution Roadmap Back to the Field Guide