PURPOSE
Builds a hard, fully-opaque ground surface layer for the parallax system. The layer is drawn at a high parallax factor (typically 0.8..0.95) so the camera reads as skimming a surface rather than looking down from orbit. Noise modulates RGB around a tint while alpha stays at 255 so one pass paints the entire viewport. Used by the old_earth biome to provide a hard ground texture underneath the square grid layer.
OWNS
GroundTextureLayerConfiginterface —id,slot,parallax,depth,tint(palette slot name or hex), optionalbrightnessRange(default 28),scale(default 2.2),octaves(default 4).createGroundTextureLayer(config)— factory returning aParallaxLayerwith adraw(frame)closure.disposeGroundCache()— clears the noise-canvas cache.- Private module-level
_cache: Map<string, HTMLCanvasElement>keyed by${tintHex}:${octaves}:${brightnessRange}. - Private helpers
resolveTint,parseHex,makeTileableGroundCanvas,getGroundCanvas. - A local tileable value-noise implementation:
hash,wrap,valueNoise, and an fbm accumulator overoctaves. - Fixed 512x512 noise tile generation.
READS FROM
./layer-types—ParallaxLayer,ParallaxFrametypes.../palette/palette-system—resolvePaletteSlotfor converting palette slot names to hex.../palette/palette-types—PaletteSlottype.- The
ParallaxFrameit receives at draw time:ctx,camX,camY,viewW,viewH. - Global
document(guarded withtypeof document === 'undefined'so it no-ops in non-DOM environments).
PUSHES TO
- A
CanvasRenderingContext2Dsupplied by the parallax frame — issuesdrawImagecalls (4 base tiles plus up to 4 widescreen guard tiles), wrapped inctx.save()/ctx.restore()withglobalCompositeOperation = 'source-over'andglobalAlpha = 1. - The internal
_cachemap (writes on cache miss insidegetGroundCanvas).
DOES NOT
- Does not time-drift. Scroll is purely
camX * parallaxandcamY * parallax; there is noframe.timeor wind-style animation. - Does not modulate alpha. Every pixel is
alpha=255so the layer is fully opaque. - Does not own its own canvas in the scene graph — it draws into the
ctxprovided by the parallax system each frame. - Does not register itself with any system; the caller (biome recipe) inserts the returned
ParallaxLayerinto the parallax layer list. - Does not handle resizing; the 512 px tile size and
scalefactor are fixed at construction. - Does not share its noise routine with
atmosphere-fbm-layer.ts— the algorithm is intentionally duplicated locally so the two layers can diverge.
Signals
- None. The module emits no events and exposes no observable state beyond the
_cacheit owns. Cache invalidation is manual viadisposeGroundCache().
Entry points
createGroundTextureLayer(config: GroundTextureLayerConfig): ParallaxLayer— invoked by biome recipes (e.g.biome-recipes.tsforold_earth) to construct a layer instance.ParallaxLayer.draw(frame: ParallaxFrame): void— invoked once per frame by the parallax system for the returned layer.disposeGroundCache(): void— exposed for teardown or biome swaps that want to free generated tiles.
Pattern notes
- Tint resolution checks the first character against
0x23(#) to decide between a hex literal and a palette slot name; non-hex strings flow throughresolvePaletteSlot. - Noise is value-noise on a torus:
hashis seeded byoctaves * 13.37so different octave counts produce different tiles even at the same tint;wrapensures the lattice wraps cleanly at the tile boundary so the resulting canvas tiles seamlessly. - The fbm loop starts at
amp = 0.5,f = 1, withamp *= 0.5andf *= 2per octave, samplingvalueNoise(x/size, y/size, f * 4). - Brightness modulation is symmetric:
delta = (v - 0.5) * 2 * brightnessRangeis added to all three channels (R, G, B) and clamped to[0, 255]. Same delta on all channels means noise reads as luminance variation, not chroma. - Cache key is
${tintHex}:${octaves}:${brightnessRange};scaleis intentionally excluded because scaling is applied at draw time viadrawImage, not baked into the canvas. - Per-frame offset uses double-mod (
((rawX % w) + w) % w) to handle negative camera coordinates correctly. - Tile coverage is 4 base draws covering one tile’s worth of offset, plus conditional extra draws guarded by
viewW > w - ox + wandviewH > h - oy + hto handle viewports wider or taller than two scaled tiles. - Module is SSR-safe:
makeTileableGroundCanvasreturnsnullwhendocumentis undefined, anddrawearly-returns when no canvas is available.