PURPOSE

Defines one layered parallax stack per biome. Each recipe is a BiomeParallaxRecipe produced by a factory function that composes starfield, silhouette-stamp, atmosphere-fbm, and ground-texture layers into a 4-slot sandwich (back / mid / near + fg above the gameplay layer). Biomes specify shape (layer count, stamp families, densities, parallax speeds); palettes resolve the abstract slot names to actual color.

OWNS

  • Per-biome layer composition for: landing_site, sunrise_city, the_voidstar, obsidian_spire, jungle_canopy, crystal_cavern, delphi, old_earth.
  • The BLUR_PX constant — depth-of-field blur radii per slot (back: 4, mid: 2, near: 1, fg: 0.5).
  • Factory dispatch table _recipeFactories keyed by biome id.
  • Public lookup getBiomeRecipe(biomeId) with landing_site fallback for unknown ids.
  • Public enumeration getKnownBiomeIds().

READS FROM

  • ./layer-typesBiomeParallaxRecipe type.
  • ./starfield-layercreateStarfieldLayer factory.
  • ./silhouette-stamp-layercreateSilhouetteStampLayer factory.
  • ./atmosphere-fbm-layercreateAtmosphereFbmLayer factory.
  • ./ground-texture-layercreateGroundTextureLayer factory (used only by old_earth).

PUSHES TO

Nothing directly. Recipes are pure data consumed by the parallax/background renderer when it requests a biome’s stack via getBiomeRecipe(biomeId).

DOES NOT

  • Does not draw anything itself — recipes are descriptions, not renderers.
  • Does not resolve palette slot names to hex; that happens at draw time.
  • Does not own the gameplay layer (parallax 1.0) or the WebGL nebula behind the back slot.
  • Does not draw the lat/long grid for delphi or old_earth — those are drawn by gameplay-grid.ts after the parallax stack.
  • Does not add lit surface detail (gradients, craters) to silhouette stamps — flat-shape rule.

Signals

None. This module is pure construction — factories return plain data each time they are called, with no events or subscriptions.

Entry points

  • getBiomeRecipe(biomeId: string): BiomeParallaxRecipe — returns a freshly-constructed recipe for the given biome id; falls back to landing_site if the id is unknown.
  • getKnownBiomeIds(): string[] — returns all registered biome ids.

Recipe factories are internal (landingSite, sunriseCity, theVoidstar, obsidianSpire, jungleCanopy, crystalCavern, delphi, oldEarth) and only reachable via the dispatch table.

Pattern notes

  • Four-slot sandwich. Every recipe targets parallax ranges back (0.02–0.25), mid (0.30–0.55), near (0.60–0.95), fg (1.10–1.60). Gameplay sits at parallax 1.0 between near and fg.
  • Palette-driven color. Tints are always slot names (bg_deep, bg_haze, bg_star, shadow, midtone, plus terrain_base for old_earth ground). Swapping the palette re-skins the whole biome.
  • Nebula visibility constraint. WebGL nebula is drawn before the back slot, so back-layer fbm opacity stays low (typically ≤ 0.20) to let the nebula read through. jungle_canopy and crystal_cavern were reduced from 0.6–0.7 to ~0.20 for this reason; sunrise_city and delphi intentionally violate this with source-over fbm layers that obscure the nebula (fbm_dark_sky at 0.55, fbm_deep_water at 0.65) to create planet-atmosphere / lake-surface reads.
  • Flat-shape rule. Silhouette stamps are flat-filled with the shadow tint, pre-blurred per slot via BLUR_PX, no gradients, no internal detail.
  • Unique seeds per starfield. Multiple starfield layers in the same recipe use distinct seeds (e.g. 1001 / 2002 / 3003 for landing_site, 3501 / 3502 / 3503 for delphi) so the layers look like different starfields, not one pattern at three parallax offsets.
  • Composite modes. Default is unspecified (additive draw). Explicit source-over is used to obscure the nebula (sunrise_city dark sky and cloud bands, delphi deep-water base). lighter is used for the Delphi ripple haze. screen is used for Delphi specular reflection / chrome sheen / chrome flicker / surface shimmer to brighten without washing to white.
  • Depth-of-field via blur. Stamp layers pass BLUR_PX[slot] so far elements feel soft and near ones feel crisp.
  • Stamp scale randomization. sizeScale: [min, max] is applied on top of each stamp family’s own world-size range. Wide ranges (e.g. 0.55..1.60 for landing_site asteroids) produce chaotic belt reads rather than uniform tile placement.
  • Minimal-stack biome. old_earth deliberately ships only a single groundTextureLayer at parallax 0.90 — no back, no mid, no fg, no stars, no silhouettes — to read as a low-altitude ship-skimming-dirt view. Ground alpha is always 255 so it fully obscures the nebula.
  • Factory invocation per call. getBiomeRecipe invokes the factory each time, returning a new recipe object — recipes are not cached.
  • Fallback id. Unknown biome ids resolve to landing_site, not an error or empty stack.