← All work
Simulation · Fundamental

Habitat

A simulation game built on Fundamental: living ecology, species as data, runtime crossbreeding. The engine's live proof, running under its own roof.

A cozy, simulation-driven garden game in a single HTML page. Three.js renders a floating storybook island; a real ecological simulation drives everything you see — moisture moves through soil layers, temperature and daylight gate growth, weeds compete, pollinators help, and seasons rewrite the world. New plants and decor are data, not code: genomes that the procedural builders interpret, which is what lets crossbreeding and AI discovery mint brand-new content at runtime.

Built on @fundamental-engine/three — the reciprocal field engine drives the pollen swarm, breeds hybrids, and steers critters.

Play: open index.html or run python3 -m http.server 3000.

Architecture

index.html               page shell: CSS theme, script load order
app/
  core.js                shared utilities (RNG, neighbor offsets, toast/float events)
  species.js             ── DATA ── genome registries + sanitizers + crossbreed/lineage + AI generation
  world.js               ── MODEL ── game state, simulation tick, ecology, economy, save/load
  engine.js              ── VIEW ──  renderer, camera/controls, sky, day/night lighting
  plants.js              genome  plant mesh builders + shared procedural textures
  tiles.js               tile meshes (INSTANCED: soil horizons, slabs, grass), ground/water shaders, decor, pipes, picking & tools
  field.js               POLLEN FIELD + FieldKit — the @fundamental-engine force field as a game engine (see docs/FieldKit.md)
  fundamental-engine-three.js  vendored @fundamental-engine/three@0.7.0 particle+force engine
  effects.js             particle systems: action bursts, rain, snow
  critters.js            the whole live ecology: bees/butterflies/fish + bunnies, fox, owl, hawk, heron, frogs, gnats, fireflies
  thumbs.js              offscreen render-to-image of a species mesh for Dex cards (window.SpeciesThumb)
  audio.js               synthesized SFX + 3 bundled .sf2 instruments + day/night/weather ambience
  ui.jsx                 ── HUD ──  React: status bar, toolbar, inspector, panels, Tweaks
  store.jsx              General Store panel + plumbing overlay
  market.jsx             Seed Market panel (buy, crossbreed, AI discovery)
  dex.jsx                The Dex panel: catalogue + genome charts + family tree + breeding bench
  tweaks-panel.jsx       Tweaks shell (starter component)
  *.sf2                  bundled piano/guitar/violin soundfonts (parsed in-browser, no CDN)
docs/                    deep-dive design docs (read these — they are the spec)
  How-It-Works.md        the conceptual spine — read this first
  Methodology.md         how to couple systems to the field & build reactive behavior (process)
  Backlog.md             design risks, open questions & fixes (counterweight to How-It-Works)
  FieldUI-Guide.md       extensive guide to the fundamental-engine (setup, forces, sensing, perf)
  FieldUI-Opportunities.md  roadmap for leaning harder on fundamental-engine, mapped to the backlog
  FieldUI-Engine-Features.md  engine capability spec (project-agnostic — for the engine lib itself)
  FieldKit.md            the field-as-game-engine API + engine gaps/wishlist
  Ecology.md             thrive/die model, animals, food web, night chorus
  Genetics.md            genome schema, inheritance rules, pollination-driven hybridization
  Appearance-Model.md    how a genome renders across lifecycle/season/weather/field (the body-vs-organ model)
  Migration.md           the moving-day manifest — state, debt, tuning risks, fundamental-engine upgrade map, cleanup checklist
  Dex-and-TileInfo.md    Dex + tile-inspector UI spec
  Design-Guidelines.md   UI design system (cream/amber storybook HUD) + layout/craft rules
  Shaders-and-Lighting.md  the painterly look — warm/cool grade, backlit grass & foliage, INSTANCED tiles
  Visual-Vocabulary.md   the visual language (world + HUD) — the why/feel behind the system

Load order (matters!)

three.js core.js species.js world.js audio.js plants.js engine.js tiles.js effects.js critters.js thumbs.js fundamental-engine-three.js field.js Engine.start() React (tweaks-panel/market/store/dex/ui).

species.js must precede world.js (the world references live genome registries). world.js must precede engine.js (the frame loop ticks the world). field.js loads after the engine/critters (it reads window.Engine/World/FieldUI).

Within a module, declaration order matters too. Modules are one big IIFE; a const mat = makeShader(...) that reads a const uniforms defined lower in the file is a temporal-dead-zone crash that takes out the whole module. Define shared uniforms before any material that closes over them.

Data flow

user input ──► tiles.js (picking) ──► World mutation ──► touch(x,z) ──► tile rebuild

frame loop ──► World.tick(dt) ── simulation ──┴──► notify() ──► React HUD re-render
                                                         └────► debounced save
  • World.touch(x, z) — a tile changed; the renderer rebuilds that tile's meshes. (Instanced surfaces — soil horizons, slabs — re-record into their shared buffers and rebuild once per frame; only per-tile contents live in a disposable group.)
  • World.notify() — HUD-level state changed (points, weather, inventory…).
  • Both schedule a debounced save (2.5 s); saves also run every 15 s, on tab hide, pagehide, and before unload. Save key: verdant-garden-save-v2 in localStorage.

The ecology (the heart of the game)

Life runs on one reusable rule — suitability vigor (see docs/Ecology.md and docs/Genetics.md for the full spec):

  • World.suitability(tile, def) / grassSuitability / weedSuitability score how well conditions suit an organism (temp × moisture × soil quality). A per-occupant vigor drifts toward suitability − rivalSuitability; high vigor spreads, low vigor dies. Clover vs grass, weeds vs crops, and dirt-greening all use this.
  • Nutrient loop: growing crops draw down soil quality; harvests deplete; compost, leaf-fall, mushrooms (t.fungus) and animal kills (World.enrich) replenish it.
  • Food web (critters.js): bunnies forage grown veggies (skipping fenced beds) fox/owl/hawk hunt bunnies (day & night), heron hunts frogs/fish. Each predator emits a FieldKit predator body so prey flee via the field. Populations are vigor-driven, not capped. Activity patterns (diurnal/nocturnal/crepuscular) gate who's awake.
  • The field is the attractor: blooms/ripe/groves are wells, sprouts draw gently; pollen density (PollenField.densityAt) drives growth speed, hybridization, music, and critter steering. Crossbreeding happens naturally where different blooms meet in dense pollen — World calls Species.crossbreed and stamps lineage.
  • Night chorus: a wet spell beside a pond brings frogs + gnats + brighter fireflies.

The simulation (world.js tick)

Per in-game hour, per tile:

  • Moisture in three layers / soil horizons — surface=topsoil (rain/watering in, evaporation out), shallow=subsoil (percolates down, roots drink, diffuses to neighbors, pond seepage), deep=substratum (settles toward the water table; capillary rise feeds shallow). All per-hour rates live in the SOIL table at the top of world.js (perc1/perc2, capThresh/capRate, gwSettle, seep, plantDraw, qRetention). The soil column renders these layers as tinted horizons (see docs/Ecology.md).
  • Temperature = season base + diurnal swing + weather. Each species has a comfort range: cold stalls or frost-wilts tender plants; heat stresses unless watered.
  • Lifecycles — annuals age & die (can self-seed), biennials bolt, perennials go dormant in winter. Growth needs daylight; clouds slow it.
  • Weeds — bare dirt invites them; mature weeds invade neighbors and planted beds (choking growth). They only run in spring/summer. Pull them with the hand/gather tool.
  • Irrigation — pipes BFS from ponds (computeFed); sprinklers need supply and a schedule window (dawn/dusk/both), freeze all winter, and can waterlog beds (root rot).
  • Economy — harvests pay (× clump count); seed drops back to your pouch; dawn stipend per living plant; honey from hives near blooms; expansion costs .

Genomes (species.js)

Plant genome: { id, name, emoji, cost, harvest, cycle, stages, growHours, temp, form: { type: stalk|rosette|branching|vine, … }, ecology: { bloom, shade, selfSeed, seedDrop, spread } }. The four form types are interpreted by plants.js; ecology traits plug into the simulation automatically — any new genome fully participates.

Decor genome: { id, name, desc, cost, seasons?, form: { parts: [up to 12 primitive parts], glow, attract } }. Parts (box|sphere|cylinder|cone|ico|torus + size/pos/color/ water/glow) are assembled by tiles.js, which also renders each custom item's 3D catalog thumbnail (window.DecorThumb).

New genomes come from: crossbreeding (local mutation blend, 5), AI discovery (10, once/day), and General-Store backorders (8, once/day, delivered at dawn). All AI output is strictly sanitized — every field clamped/whitelisted — with a procedural fallback so a failed call never wastes the spend.

Controls

  • Orbit right-drag (or drag on empty sky) · pan WASD/arrows, drag, middle-drag
  • rotate Q/E · zoom scroll, +/− · tools number keys or toolbar
  • Drag across tiles to paint with most tools. Right side panels: Store, Market, Weather.
  • Photo mode hides the HUD and slowly orbits. Esc exits.

Development

Conventions

  • Conventional commits. feat(scope): …, fix(scope): …, docs, chore, refactor.
  • Modules are IIFEs exposing one window.X global; no build step, no imports.
  • All randomness that must be stable across rebuilds is seeded (Core.rng(seed)).
  • Materials are cached/shared wherever tiles repeat them (see tiles.js TILE_MATS).
  • Repeated tile geometry is instanced — soil horizons (3 InstancedMesh, moisture- tinted via instanceColor), top slabs (one InstancedMesh per tile-type, custom top shaders gated on #ifdef USE_INSTANCING), and grass blades (one mesh, all blades).
  • UI strings live in the component files; simulation tunables sit at the top of world.js (SEASON_*, SOIL, POLLEN, costs) and species.js (genome clamps).

Verification

Load index.html in a browser, check the console for errors. The field should render a pollen swarm around blooming plants; critters should move; the soil horizons should darken with moisture.

Built with

License

MIT © Zach Shallbetter