Game Mower wiki

Game

Grass: the lawn itselfGR

The lawn itself, cell by cell. How it bends and stands back up, how weather, sun and the way you mow change it over days, and what each condition does to a mower.

The grass is alive. It bends under your feet and the mower and stands back up, it shows stripes that flip as you walk round, and every 0.3 m cell has its own condition: wet, lush, shady, scorched or flattened by rain. Two cells a metre apart can mow completely differently, and the lawn remembers how you treated it.

The player's view

A morning job. The lawn is silvery with dew and your steps leave dark green trails where the dew was knocked off. Push the mower in and the deck shoves a bow wave of grass ahead of it, the wheels press two lines, and the grass shivers round the deck in the blade's wind until you let go of the bar. At 8:00 the wet grass is heavy and clumpy; come back at noon and the same lawn mows light.

Stripes. Walk to the other end of the lawn you just striped and every band flips, light to dark. They're boldest when you look low across the lawn and almost vanish from straight above. A diagonal pattern keeps clean edges, and if you come back the same day the bands are still crisp.

A week later. The sunny side has grown taller than the shady corner. After a storm, the long grass lies flat with the wind, and a pass against the lay catches what a pass along it misses.

A heat wave. The strip along the drive browns first, then the mound, while the strip in the house's shadow stays green and the grass under the tree goes thin and dry. Your footprints stay in the brown grass: the lawn needs water. Mowing high helps the next day; mowing short makes it worse, and three weeks of it kill the turf.

Every patch sounds and looks different. A dewy patch squelches and spits clumps, a scorched one crackles and puffs dust, weeds whip and shed fluff. Tall grass carries seed heads, a sunny dewy morning glitters, and yesterday's clippings have turned straw-coloured.

Ideas

GR1: grass that moves

Cosmetic, so the same on every preset and nothing to sync. The lawn reacts to whatever moves through it, then recovers.

The trample field

  • A top-down texture holding a push direction, an amount and an age for the grass round you. Anything moving stamps into it every frame; a fade pass brings it back to zero. Grass.shader reads it next to GM_WindOffset and bends each tuft away from the push, its shadow too (or bent grass casts upright shadows). No chunk rebuilds, one texture read per vertex.
  • It is one 51 m window round the local player, ~10 cm a texel, that wraps round as you walk. It covers every lawn near you: client gardens, town front lawns and grandma's lawn seen from the hub (YardGrass reads it too).
  • Recovery by height: short grass (≤ 5 cm) is back in ~3 s, 8 cm in ~10 s, tall grass (15 cm+) in ~40 s. A wet lawn recovers ×1.5 slower; a drought-stressed one ×(1 + 3 × scorch) slower (the footprint test, GR2). Each tuft works out its own recovery from its height, so the field never needs to know what grows where.
  • The feel: a push eases in over ~0.15 s instead of snapping (a foot stays down 0.2 s, a deck holds its push); the field is blurred a little round each tuft and stamps have soft edges; the blow's lean and the trample add up as vectors. A tuft that gets cut shrinks from its old height to its cut height over 0.45 s instead of popping.
Walk through the lawn with the pointer, push the mower, let the dog out: the grass bends and stands back up
  • Who stampsYour feet (a print each stride, pushed the way you walk, harder when running), the other sibling and strolling clients the same way; the mower's bow wave ahead of the deck, the deck and its two wheel lines; a rolling ball. The dog and the cat get the same stamper once they roam the garden (for now they sit outside the fence).
  • Dew trailsWhile the lawn is wet (dew until ~11:00, longer in shade; DY3, WE3), the dewy lawn has a silvery sheen and a trampled cell shows the plain green where the dew was knocked off. The trail fades as the lawn dries. A morning lawn shows where everyone walked.
  • Tall grassA rustle that grows with height and speed; a slower walk on foot, ×0.9 at 20 cm down to ×0.75 from 35 cm; a puff of seeds from tall grass you brush past (from 20 cm).
  • Spring-back plugs inMowing.md MO6's laid cells write into the same field with a 2-5 minute hold instead of the normal fade, then pop up as tufts. One system for the look; "laid" stays the gameplay rule.
  • The limitOne tuft per 0.3 m cell, so a footprint bends about one tuft rather than drawing a crisp print. It reads fine in the chunky style.

The blade's blow

The air leaves the deck through its openings, like the stones (mowing.md MO3), and makes the grass round the deck lean out and flutter.

  • A ring round each running deck (up to 4), worked out in the grass shader: no field channel, so no shimmer trail behind the deck. It is stronger at the front gap on the discharge side with a bag fitted (most of the air goes into the bag: a soft ripple), out of the discharge without one (a blast straight behind), a long jet to the right from a side chute, and the grass ahead blows flat when the deck is tipped up.
  • The grass leans out from the deck with the air (up to ~30° at full blow) and flutters a little on top, showing the paler undersides of the blades.
  • Strength = (blade speed)², from MowerVisual.BladeSpeed01 (~0.9 s to spin up, ~2.2 s to run down; guests run it from the blades-on flag). It builds as the blade spins up and dies away as it runs down: letting go of the bar fades it over the 2-3 s run-down (engine-stop bar) or in well under a second (brake clutch), and a bogging engine weakens it. ×1.4 with the high-lift blade, ×0.6 on slow throttle.

Mowing stripes

Stripes are light, not geometry. Grass bent away from you shows the long sides of its blades, which catch the sky (a light band); grass bent towards you shows tips and shaded undersides (a dark band). Walk to the other end and every band flips. No new model is needed: it's shading plus a fine record of which way the grass lies.

Why a stripe map, and not the old per-cell lie (each cut cell scaled its colour by 1 + dot(lie, view) × 0.24):

  1. Blocky band edges: a deck is ~1.5 cells wide, so edges followed the grid and a diagonal pass became a staircase.
  2. Same contrast from every height: real stripes nearly vanish looking straight down and are strongest looking low across the lawn.
  3. Flat brightening: real bands get their glow from the sky on the blades and their darkness from shaded undersides, so they read best with a low sun and soften under cloud.
  4. A roller looked like a rotary, while real roller stripes are much bolder.
  • The stripe map (StripeMap): a top-down texture at 7.5 cm a texel (a 4 × 4 record per cell), direction + amount, written as you push by the deck's cut circle swept along your path. Band edges are straight lines along your real path, diagonals stay clean, an overlap takes the last pass's direction. The grass and the stripe mat sample it per pixel. The per-cell lie in LawnGrid stays the sim's truth (streak, grain, memory); the map is the look.
  • Shading, with d = how much the grass lies away from the camera (−1..1) and g = how low you look ((1 − |view.y|)^0.8, floor 0.15):
    • brightness 1 + k × g × light × d (k = 0.30 rotary, 0.60 roller);
    • a sky sheen on light bands (a pale sky tint × 0.12 × d × g) and shaded undersides on dark ones (−10% × |d| × g);
    • light: ×1.2 with a low sun, ×1 at midday, ×0.6 overcast (LawnWeather, DY3's sun).
  • Roller ×2 contrast, lasting 4 cm of regrowth.
  • Patterns (garden.md GA6: checkerboards, diagonals, circles) come out crisp for free.
  • Co-op: each machine draws the stripes of every mower it sees: its own through the cut, the others from their avatars. Cells changed some other way (a merge, the trimmer, lawn care) are brought back in line with the grid after 0.4 s.
  • Coming back crisp: the bands are usually gone by the next visit (a rotary's after ~1.5 cm of regrowth, about a day; a roller's after ~4 cm, about three days), but a quick return would show blocky bands rebuilt from per-cell values. A band edge always runs parallel to the stripe, so each cell only needs where the edge crosses it: a 3-bit band-edge offset in lawn memory (eighths of a cell, 0 = no edge). On load the map is rebuilt from the cell directions plus the offsets, with straight edges (bit layout in Tech).
The same lawn before GR1 (left) and with the stripe map (right): walk round it and look down

Needs: MO1 · Done when: the bands flip as you walk round a freshly striped lawn, are strongest looking low across it and faint from straight above, a diagonal pattern has clean edges, and leaving and coming back the same day shows the same crisp bands; walking through 15 cm grass leaves a trail that stands back up over about 40 s, the mower pushes a bow wave, the grass shivers round the deck until the blade stops, a morning lawn shows dark dew trails, and the guest's footsteps bend the grass on the host's screen.

GR2: grass condition and scorch

Every cell has a condition, built from the weather, the cell's own grass and its place in the garden. The mower reads the condition, never the weather.

Pluggable to weather: three layers

  1. Lawn weather (LawnWeather, per hour, the whole garden): the values the lawn reads from weather.md, nothing computed here. Wetness, soil moisture, temperature, heavy rain in the last 24 h, the wind's direction, and frost, heat and leaf fall.
  2. Grass condition (GrassCondition, per cell, derived, never saved): the lawn weather + the cell's base (seeded patch, thickness, GA2 health) + its lasting conditions from lawn memory + its site. Recomputed per chunk when the hour or the cell changes.
  3. The mower's response (GrassResponse): a data table, one row per condition (the response matrix below). MowerSim and LawnGrid read the cell's combined response.
  • Until weather.md WE3, the lawn weather comes from the calendar: wetness from dew and rain, soil moisture from the last ten days' rain, heat 0.35 in a dry streak of 4+ days in spring and summer, heavy rain on half the rain days, a wind direction per day. The sandbox can override it.
  • New weather later (a cold snap WE10, a drought WE11) = weather.md fills a lawn-weather value and the matrix gets or tunes a row. No mower code. Heat waves wait for WE11.
  • Wet keeps loading the deck through the mower's humidity, as before GR2: the matrix's wet row only drives grip, clog, sound and spray, so job balance doesn't move. "Lush" comes from feed and soaked soil only (thickness already sets the mass).

Every cell has its own condition

  • InstantWet, frost, leaf cover. Recomputed from the hour's lawn weather, gone when the weather changes.
  • LastingScorch, days fully brown and the usual height (this idea); moss and thatch (GA2); frost marks; scalps and scuffs (MO1); smothered (MO6). They build up over days, live in lawn memory, and a daily tick (LawnDay) updates them from the day's lawn weather.
  • The site mapEach cell's sun exposure, paving edge, mound or dip, and tree roots. Worked out from the garden's layout, never saved.

The site map, per cell (Garden/GardenSite.cs):

  • Sun exposure (0-1, the share of the day in direct sun): six positions of DayCycle's sun arc, a raycast per cell, past the house, trees, hedges, fences and sheds. Tree shade is dappled: a canopy lets ~25% of the light through (deciduous trees far more when bare, with seasons); hedges, walls and houses block it all. Neighbours count: the houses next door and street trees within ~15 m. Shade keeps dew and frost longer and slows growth; sun scorches; damp shade grows moss (GA2).
  • Paving edge: lawn within 0.6 m of a hard surface (mowing.md MO2: drive, path, patio, slabs).
  • Mound or dip: from the grid's bumps and wet dips.
  • Tree roots: within 1.5× a tree's canopy radius.

Scorch: sun-exposed grass browns in dry heat

Real turf lore, kept close: grass browns first where it's hottest and driest, and long grass survives heat best (its blades shade the crown and the soil, and it grows deeper roots). Lawns usually go dormant, not dead: brown, then green again after rain.

Each day, in the daily tick:

scorch += 0.12 × heat × site heat × exposure × (1 − moisture) × shade(height now) × roots(usual height) × torn tips

  • Heat1 in a heat wave, 0.35 in a dry streak (4+ dry days), else 0.
  • MoistureThe lawn weather's soil moisture, times the site: mound top ×0.6, dip +0.3, under a tree ×0.5 (its roots take the water: dry shade), paving edge ×0.7 (thin soil). Watered: at least 0.7.
  • Site heatPaving edge ×1.4 (heat comes off the stone). Brown strips along the drive come first.
  • Height now: shadeThe height after the last cut. 3 cm ×1.5, 5 cm ×1, 8 cm ×0.7, 10 cm+ ×0.6 (linear between). Raising the blade helps from the next day.
  • Usual height: rootsThe running average of the height after the last 4 cuts. 3 cm ×1.3, 5 cm ×1, 8 cm ×0.8, 10 cm+ ×0.75. Raising the blade once helps the shade at once, the roots only over weeks. It starts at the client's height and is kept in whole cm.
  • Torn tips (0-3, from a dull blade, MO1): ×(1 + 0.15 per level). Ragged tips lose water.
  • Heals with soil moisture: −0.08 × clamp((moisture − 0.3) / 0.4) a day (−0.08 in rain, about −0.04 on a normal day), −0.2 a day watered; never below 0.
  • Caps: 0.3 in a dry streak (the mild version, possible now in late spring); 1 in a heat wave.
  • Dormant from 0.6: brown but alive, green again 1-3 weeks after rain or watering.
  • Dead: lawn memory counts the days fully brown (scorch 1) in dry heat. After 12, the crowns die and the cell goes bare (the existing bare patches): only seed brings it back (GA2 overseeding, the seed shaker). A scalp on dormant turf leaves bare soil too.
  • What it adds up to: a week of heat wave in full sun on dry soil reaches ~0.6-0.8 (dormant, recovers). Three weeks at a normal height kill only the hottest strip (the drive edge); cut short, most of the sunny lawn dies; kept long, almost none. The strip in the house's shadow stays green throughout.
  • The footprint test (a real gardener's test): on drought-stressed grass, footprints and wheel tracks stay. The trample field recovers ×(1 + 3 × scorch) slower, so at 0.6 your prints last three times as long (the field remembers 180 s). If your footprints stay, the lawn needs water: a hint with no HUD.
  • Look: the shader's per-cell dry value is max(dry patch, scorch): straw from ~0.4, brown past 0.75; dormant tufts droop a little; dead cells draw as bare soil.
  • Work it creates: clients notice brown patches (and blame the mower if you scalped in the heat); watering (a sprinkler, a hose job: WE14); reseeding dead cells (GA2); feed on scorch above 0.3 burns (GA2).
A back garden through a dry spell: pick the weather, the cutting height and the care, and watch where it browns

Sun, shade and growth: an uneven lawn

  • Growth follows the sun: each cell's daily growth × (0.6 + 0.4 × exposure), on top of its own vigour and patch growth (dandelions faster, dry patches slower). Density too: thickness × (0.7 + 0.3 × exposure), and the shader pales and thins shady tufts.
  • So heights go uneven between visits: on a normal week, ~10 cm of regrowth in full sun against ~7 cm in deep shade, varying smoothly, and every cut levels it again. The mower feels it (load follows the mass), shady corners need mowing less often but look patchier, and moss sets in there (GA2).

Rain-flattened grass (lodging)

  • After heavy rain in the last day, uncut grass taller than ~12 cm lies flat in patches (seeded noise), wet and lush cells most, laid the way the wind blew.
  • It mows like spring-back's laid grass (mowing.md MO6): each pass along the lay leaves 40-60% of the excess (one try per pass, so a second pass along catches more), tufts popping up minutes later; a pass against the lay takes it all. It stands back up once the rain is two days old.
  • Derived from the recent weather and the cell's height: nothing saved. Drawn through the trample field's hold. Storm clean-up work comes with it (WE8).

The response matrix

Each condition has a strength 0-1 (wetness, scorch, weed cover…); a factor f applies as 1 + (f − 1) × strength. Starting numbers, tuned in the sandbox. - = no effect.

Condition (strength)MassBag fillClogDustGrip on turnsDeck bobLeft standingScalp harmAfter-markCut soundOff the deck
Wet (wetness)×1.6×1.5×1.8, clumps above 0.5none−40%--×1clumps smothersquelch, wet slapwet spray, clumps
Dry patch×0.6×0.8×0.8×1.5---×2-crispdust puffs
Scorch (dormant from 0.6)×0.5×0.7×0.7×2.5---×3; above 0.6 barestraw / brown tintdry crackledust, straw bits
Dead (bare)×0--×2----needs seedgritty scrapedust, soil
Lush (feed, soaked soil)×1.4×1.3×1.2----×0.8-thick, the engine laboursdense green spray
Shade (1 − exposure)×0.7×0.8-----×1paler, thinnersoft, sparselight spray
Clover×0.85×0.9--−20% when wet--×1-softleaf bits, flower heads
Coarse weeds×1.8×1.2×1.3---stalks (dull blade, sharpness < 0.5)×1-stringy whipstalk bits, dandelion fluff
Seed heads×1.1-----30% spring back (sharpness < 0.6)--tick of stalksseed fluff
Lodged (rain-flattened)×1.1-×1.2---40-60% of the excess, any blade; stands back up--uneven swishpatchy spray
Thatch×1.2×1.1×1.2--±1 cm-×2 on soft spots-muffled thudbrown fibres
Moss×0.4×1.3 (bulky)--−35%--×1-soft hushmoss crumbs
Frost×1---−25%---black prints ~3 dayscrunchice glitter
Leaves (cover)×1.5×2×1.5 (×2.5 wet)-−15%----rustling shredleaf confetti
New turf×0.8------scalp = bareruts ×2softfine green
  • Stacking: mass, bag fill, clog, dust and scalp harm multiply; grip multiplies what's left (−40% and −20% = ×0.48); deck bob takes the largest; wet over 0.3 cancels dust.
  • Clean speed isn't a column: it comes from the combined mass (CleanSpeed = optimal / √mass). A dewy, lush, weedy cell at 8:00 (wet 0.6, lush 0.5, weeds 1): mass ×2.9, clean speed ×0.58.
  • Mass is per cm of grass removed, so the cell's height already counts. Load and fuel follow the mass; dust wears the air filter (mowing.md MO5); grip feeds the slip on turns; scalp harm scales how long a scalp stays brown.
  • Cut sound and off the deck feed CutAudio (its brightness) and MowerVfx (the spray colour), layered by the conditions under the deck, weighted by strength, plus a dust haze over dusty grass. Thatch, moss, frost, leaves and new turf are neutral rows until their ideas come (GA2, seasons, GA5).
Try a cell: pick the time, the last days' weather, sun or shade and the patch, and read the mower's response

The conditions

ConditionComes fromUnder the mowerAfterwardsComes with
Wet (dew, rain)wetness, longer in shadeheavier, slower clean speed, bag fills ×1.5, clumps above ~50%, deck caking, slips on turns, ruts on soft groundclumps smother (MO6)GR2
Dry, droughtdry patches (seeded), low soil moisture, heatlight (×0.6), cuts easily, dust: the air filter clogs soonercut low, it browns twice as longGR2
Scorched, dormant (lasting)sun × heat × site × height, day by dayvery light (×0.5), very dusty, fragile; footprints staystraw to brown; above 0.6 a scalp leaves bare soilGR2 (mild); full with heat waves
Dead (lasting)12 days fully brown in dry heatnothing to cut, dustbare until reseededGR2
Lush (fed, a wet week)feed (GA2), soaked soilheavier, bag fills sooner, slower clean speed-GR2
Shadylow sun exposure (house, trees dappled, neighbours)lighter, sparser; grows ~30% slowerpaler, thinner; moss later (GA2)GR2
Lodgedheavy rain on grass over ~12 cm, laid with the winda weak cut, tufts pop up; a pass against the laystands back up in two daysGR2
Cloverseeded patcha little lighter; slippery when wet-before GR1
Coarse weeds (plantain, dandelion stalks)seeded, GA2 weedsheavy (×1.8), stalks stand back up behind a dull blade-GR2
Seed headslate springwiry stalks a dull blade only bends: they spring back like laid grass-GR2, sharpness from MO5
ThatchGA2the deck bobs, an uneven cut, scalps on the soft spots, more load; roots dry fast-GA2
MossGA2 (damp shade)wheels slip, little to cut, the bag fills with moss bits-GA2
Frostfrost mornings (WE10: no mowing until it thaws)mowing or walking on it anyway turns the crushed blades black (the trample field shows the prints)black for ~3 days (lawn memory); clients noticeseasons
Leavesleaf fall (autumn); LeafFieldmulched or bagged: the bag fills fast, wet leaves clog-seasons
New turf, new seedGA5 new lawns, sown cellsfirst cut high only; soft ground rutsa scalp kills itGA5

Feedback, all in the world: dew shows in the dark trails and the squelch; dust as a haze behind the deck; frost as a white sheen; scorch as the straw-brown colour of the cells; drought as footprints that stay.

Needs: GR1, MO2 (paving edges) · Done when: the same lawn mows heavy and clumpy at 8:00 and light at noon by changing only the sandbox's weather, and a sandbox heat wave browns the drive edge and the mound first, leaves the strip in the house's shadow green, thins the grass under the tree, and keeps your footprints; a week after mowing, the sunny side is visibly taller than the shady corner; after a storm, the long grass lies flat with the wind; and a dewy patch squelches while a scorched one crackles.

GR3: the grass look

The chunky tuft matches the art direction (ArtSource/STYLE_GUIDE.md) and the budget, so the style stays. These are targeted additions, each picked by the cell's hash, its state and the chunk's distance (LawnRenderer; YardGrass picks variants by hash and the far tuft by distance, so town front lawns get them too).

ModelWhat it isWhy
A mid joint on every blade (GR1)9 wedge blades, 81 trianglestrample, laid grass and the blow bend each blade as a curve instead of tilting stiff sticks
Tuft variantsGrassTuft_B (81 triangles), GrassTuft_C (ten blades, 90)a cut lawn seen from above showed the repeat of one mesh; random yaw and scale hid it only partly
A seed-head tuftGrassTuft_Seed: six blades + three wiry stalks with heads, for uncut grass from 18 cm in spring and summerthe seed heads condition and the seed puff need something to see
A blunt cut tuftGrassTuft_Cut: squared-off tips, on every cut cellcut and uncut read by shape, not only by colour
A far tuftGrassTuft_Far: three blades, 27 triangles, for distant chunksa 4,000 m² estate is 44,000 tufts: ~3.6 M triangles at 81 each if all were near
Side views of the tufts: jointed, bent, a variant, seed heads, blunt cut, far
  • Shader touchesDormant droop, scorch tint and shade paling (GR2), flutter undersides (GR1). Colour and a little droop from per-cell data, no mesh.
  • Dew glintTiny sparkles on wet tips (not where the dew was knocked off), stronger with a low sun, none under cloud, twinkling as you move. A morning lawn says "still wet" without a HUD.
  • Clippings dry outLeft on the lawn they go from green to straw over ~20 real minutes (about half a game day); on paving they stay until swept.
  • Mulched grass: no modelA mulching mower's clippings are meant to vanish. A faint fine speckle (the clippings mesh, tiny, darkened) shrinks and sinks over 60 s.
  • Clumps live elsewhereWet clumps and an overloaded mulcher's ragged trail use the clump model from mowing.md MO6.

Performance target and the grass detail setting

  • Target: 60 fps at 1080p with a 4,000 m² lawn in view on the minimum-spec machine (not defined yet; until it is, the dev Mac with the setting on Low).
  • Grass detail in Settings (Low / Medium / High; per machine, display only, nothing synced in co-op; older settings read Medium):
LowMediumHigh
Full tufts up to (then the far tuft)15 m30 m60 m
Trample field texel20 cm10 cm7.5 cm
Tuft variants13 + seed heads3 + seed heads
Dew glint, flutter undersides, bent shadowsoffonon
  • The stripe map stays 7.5 cm on every setting: it's the lawn's look of the sim, and small. The sim never changes with the setting: the same cells, conditions and responses on every machine.

Needs: GR1 · Done when: a freshly cut lawn seen from above shows no repeating pattern, tall grass in spring carries seed heads, a sunny dewy morning glitters, yesterday's clippings look straw-coloured, and a 4,000 m² estate holds 60 fps at 1080p on the min-spec machine (or the dev Mac on Low until min spec is defined).

GR4: soil, species and other scorch factors

More of the turf lore, when seasons or GA2 need it: soil type per garden (sandy dries fast, clay holds water but compacts), grass species per garden (some brown fast, some tolerate drought), a drying wind, and dog spots (a brown spot with a dark green ring: not heat, a lawn mark of its own).

GR5: the rest of grass condition

Three pieces of GR2 left for later: the one-third rule (cutting more than a third of the height on a heat day: scorch ×1.5 that day), scalp-harm durations from the matrix (a scalp stays brown 7 days × its scalp harm), and the grass-condition and site maps as overlays in the sandbox (today it shows the cell under the deck).

GR6: grandma's lawn from the hub remembers

The hub's view of grandma's lawn reads that lawn's memory file, so scorch, uneven growth and stripes there match what you left (it shows the height and the look today). Town front lawns stay decorative (nobody mows them), maybe with a seasonal tint from the same lawn weather.

  • What it shows, per cell: the height (a missed strip stands tall, a fence fringe grows shaggier), the stripes (direction and strength, fading with the regrowth as in MO1), scalps, scuffs and scorch in brown, and bare cells. Torn tips, ruts and laid grass are too fine to read from the house and stay out.
  • How: grandma's lawn is rebuilt the way a visit builds it (the seed, the remembered lawn, AgeDays up to today) without the renderer, then each YardGrass tuft takes its nearest cell's height and look. The coarse looks of step 4 (HomeLawnLook: striped, neat, shaggy, jungle) keep driving the story (the doormat notes, the street's standing); the drawing comes from the cells.
  • Before the first mow there's no memory: the lawn is drawn from the seed at its start height, the jungle you wake up to.
  • When it refreshes: when the hub loads, each morning (a day's growth), and when the mow ends (the memory was just written). Never per frame: the rebuild is under a few milliseconds, and the hub's frame budget (GR3) doesn't move.
  • With J37: the resting view of every mowed lawn works this way; grandma's is the first.
  • Co-op: guests don't have the memory files. The host sends grandma's lawn blob (1-15 KB, chunked to 900 bytes like NetCompany) when a sibling joins and after each write, so both draw the same cells.

Needs: MO1, GR3 · Done when: mow grandma's lawn leaving a missed strip and a scalp, go back to the hub: the strip stands taller and the scalp shows brown where you left them, and the stripes show; the next morning it has grown and the stripes have faded; a guest sees the same cells; the hub's frame rate is unchanged.

GR7: mushrooms and fairy rings

After wet spells, mushrooms pop up, sometimes in a ring. The mower shreds them with a puff of spores.

GR8: worm casts

After rain, little mud piles on the lawn that smear under the deck.

GR9: worn paths

Where people walk every day, the grass goes thin, then bare, and the soil compacts. Aeration fixes it (GA2).

GR10: the lawn creeps

Over weeks, the lawn creeps into beds and over path edges, which gives the trimmer and edging page its work.

Tuning

Starting numbers, tuned in the sandbox. The response matrix (GR2) is its own table.

NameValueIdeaWhat it does / why
Recovery by height3 s ≤ 5 cm, 10 s at 8 cm, 40 s from 15 cmGR1how long trampled grass takes to stand back up
Wet recovery×1.5GR1wet grass stays down longer
Drought recovery×(1 + 3 × scorch)GR2the footprint test
Push ease-in / foot hold0.15 s / 0.2 sGR1pushes don't snap
Cut shrink0.45 sGR1a cut tuft shrinks instead of popping
Footprint size22 cmGR1tufts stand 30 cm apart, so a smaller print misses most of them
Bend at full push72°GR1how far trampled grass lies
Blow leanup to ~30°GR1grass leaning out of the deck at full blow
Blow: high-lift blade / slow throttle×1.4 / ×0.6GR1more air, less air
Walk in tall grass×0.9 at 20 cm to ×0.75 from 35 cmGR1gentle, on foot only
Seed pufffrom 20 cmGR1brushing past tall grass
Stripe contrast k0.30 rotary, 0.60 rollerGR1band strength
Stripe view floor / exponent0.15 / 0.8GR1faint from above, strong looking low
Sky sheen / shaded undersides0.12 / −10%GR1light and dark bands
Stripe light×1.2 low sun, ×1 midday, ×0.6 overcastGR1bands read best with a low sun
Band life~1.5 cm regrowth rotary, ~4 cm rollerGR1when stripes are gone
Scorch rate0.12 a dayGR2how fast heat browns
Heat1 heat wave, 0.35 dry streak (4+ days)GR2
Site moisturemound ×0.6, dip +0.3, tree ×0.5, paving ×0.7, watered ≥ 0.7GR2
Paving heat×1.4 within 0.6 mGR2brown strips along the drive first
Shade by height now3 cm ×1.5, 5 cm ×1, 8 cm ×0.7, 10 cm+ ×0.6GR2
Roots by usual height3 cm ×1.3, 5 cm ×1, 8 cm ×0.8, 10 cm+ ×0.75GR2over the last 4 cuts
Torn tips×(1 + 0.15 per level)GR2
Healing−0.08 × clamp((moisture − 0.3) / 0.4) a day; −0.2 wateredGR2
Caps0.3 dry streak, 1 heat waveGR2
Dormant / deadfrom 0.6 / after 12 days fully brownGR2
Canopy light~25%GR2dappled tree shade
Neighbours' shadewithin ~15 mGR2
Growth by sun×(0.6 + 0.4 × exposure)GR2~10 cm a week in sun, ~7 cm in deep shade
Density by sun×(0.7 + 0.3 × exposure)GR2
Lodginggrass over ~12 cm; 40-60% left per pass along the lay; up after 2 daysGR2
Seed headsuncut grass from 18 cm, spring and summerGR3
Clippings dry~20 real minutesGR3
Mulch specklesinks over 60 sGR3

Co-op

  1. State: the trample field is local and cosmetic (each machine stamps from the positions it already has); grass conditions are derived from the seed, the hour and the host's weather; lasting conditions live in the lawn memory blob (the host's save).
  2. Ownership: the host runs the daily LawnDay tick.
  3. Host-only: the memory write when the lawn is left.
  4. Randomness: none new (conditions are derived; tuft variants come from the cell hash).
  5. Channel: nothing new; lasting conditions travel in the memory blob sent when the lawn loads. The host's lawn snapshot carries the band-edge offsets (CellSync.bandEdge).
  6. Late joiners: the blob, as for MO1.
  7. Checks: the guest's footsteps bend the grass on the host's screen; a heat wave browns the same cells on both.

Tech

Who owns what

garden.md GA2 owns the lawn's health and its treatments (weeds, thatch, bare, colour, feed, seed); weather.md owns the weather (wetness, soil moisture, temperature, seasons, events); day.md owns the sun and dew (DY3); mowing.md owns the mower. This page owns what grass does with all of that.

Where it applies

LawnDrawn byGets
Client gardens, grandma's lawn when you mow it (the family mission), the sandboxLawnGrid + LawnRenderereverything on this page
Grandma's lawn seen from the hub (Garage/HomeLawn.cs)YardGrass at the mission's height (decorative)the look only: trample, stripes look, variants, far tuft, dew glint (GR6: what you left there)
Front lawns in townYardGrassthe look only

The per-cell parts (condition, scorch, growth by sun, lodging, the response matrix, memory) need a LawnGrid, which every lawn you mow has.

The code

  • The grid (Sim/Mowing/LawnGrid*.cs): 0.3 m cells, grass height per cell, thickness (noise), patches (clover, dry, weeds) whose mass factors live in GrassResponse, wet dips, bumps.
  • Lawn weather and condition: Sim/Mowing/LawnWeather.cs, GrassCondition, GrassResponse.cs (the matrix as data), the daily tick LawnDay; the site map in Garden/GardenSite.cs.
  • Drawing (Lawn/LawnRenderer.cs, Shaders/Grass.shader): one instanced tuft per cell (ArtSource/scripts/models.py build_grass_tuft and its variants, rebuilt through the Blender MCP), a flat stripe mat under cut cells, a clippings mesh, weed, daisy and clover meshes. Wind sway in the vertex shader (GM_WindOffset). Per-cell shader data: cut time, lean, scalped, clover, dry, wet, feed, care (_GrassCare), patch (_GrassPatch). Front lawns in town: Town/YardGrass.cs.
  • Grass that moves: the trample field (Lawn/GrassField.cs + Shaders/GrassField.shader, numbers in Sim/Mowing/GrassMotion.cs), footprints (GrassStamper), the bow wave, wheel lines and blow (MowerGrass), dew trails, tall grass (TallGrass); the stripe map (StripeMap).
  • Mulching mowers draw only the speckle.

Lawn memory: what grass adds

MO1's memory (LawnGridMemory.cs) stores one byte plane per field over the lawn cells, each deflated on its own, with small values bit-packed.

PlaneFormat 1 (MO1)Format 2 (GR1)Format 3 (GR2)
height1 byte==
stripedirection 4 bits · amount 3 bits · roller 1 bit==
marksscar 3 bits · scuff 3 bits · torn 2 bits==
miscstreak 2 bits · bare 1 bit · 5 spare+ band-edge offset 3 bits (bits 3-5), 2 spare=
feed, sown1 byte each==
scorch--scorch 4 bits (16 levels) · days fully brown 4 bits (0-12)
usual height--1 byte (whole cm)
edge cellsfringe height, coverage==

Format 4 (mowing.md MO6c) appends a habits plane: rut 2 bits · stress days 3 bits · 3 spare. Spilt fuel writes the misc plane's bare bit.

  • Older formats load: missing bits read as 0 (no band edge, no scorch, usual height = the current height). The reader accepts formats 1 up to the current one and refuses newer ones. The layout fingerprint uses a fixed LayoutVersion, not the format number: with the format in it, any bump would make every saved lawn look like another garden and drop it.
  • Room for more: the misc plane keeps 2 spare bits, and header byte 3 is written as 0 and unused (a free per-lawn flags byte). Beyond that, new data = a new length-prefixed plane appended after the others + a format bump. An all-zero plane deflates to a few bytes.
  • Size: the 4,000 m² lawn stays under the 16 KB budget (15.4 KB with format 3; MO1's size test).
  • AgeDays runs the daily tick for the days between visits.

Sandbox pieces (docs/dev/sandbox.md)

  • GR1: a "Grass that moves" section: Long-grass strip (cm) (part of the spec, so a co-op guest builds the same: a 1.5 m band of uncut grass across the middle of the lawn), Wet lawn (this machine only: forces the morning dew, to see the trails), and a live view of the trample field round you (red / green the push, blue how fresh, alpha the dew knocked off) with the stamps a frame and the dew. From eval: GrassField.Push(position, radius, direction, strength) stamps by hand; Next visit in 0 days checks that the stripe bands come back crisp.
  • GR2: a "Grass condition" section: the LawnWeather override (heat wave, heavy rain, soil moisture), skip days (the daily tick), a scorch brush at the crosshair, a readout of the cell under the deck (weather, sun, tree, paving edge, mound, scorch, brown days, lodging, the response); the condition and site overlays are that readout for now (GR5).
  • GR3: "Grass gallery": every tuft, uncut and cut, in a row in front of you (plain renderers: no stripes or bend).

Tests and checks

  • GR1 (GrassTests): recovery per height and wetness; the field's wrap-round; the walk factor; stripe shading by view height and roller; a diagonal pass leaves a straight band edge; a stamp outside the lawn is ignored; a merged cell comes back in line; a memory round trip keeps the band edges, the host's snapshot carries them, format 1 blobs load, a newer format is refused. Checked in the sandbox and in co-op (editor host + dev build guest): diagonal bands clean; tall grass laid flat by a push; a dewy lawn with a green trail; the bands back after a same-day return; the guest's mower stamping the host's field and laying sub-cell stripe edges in the host's map; guest mismatches 0.
  • GR2 (ConditionTests, 8): wet at 7:00, dry at 13:00; stacking (mass ×2.9); a heat wave browns the sun, not the shade, the paving edge first, short grass worst; a mound dries before a dip; twelve brown days kill, rain revives dormant; dappled shade grows ~30% less; lodging along vs against the lay; memory keeps scorch, older formats read none. Checked: grandma's lawn has exposure 0..1 (the house's shadow) and 36 paving-edge cells; ten sandbox heat-wave days scorch it to a mean of 0.38 (25 cm grass shades its roots); the guest's snapshot matches the host's exactly (825 scorched cells).
  • GR3: the detail setting (distances, texels, Low's cuts) and old settings reading Medium. Measured on the dev Mac (editor, Medium, a 66 × 60 m sandbox estate, 43,679 cells): 50 fps with ~37,000 instances drawn. The editor's unfocused play runs at 3-10 fps, so that is the only clean reading so far.

Open questions

  • The minimum-spec machine isn't defined anywhere yet; the grass performance target (and every other one) needs it.
  • Numbers to tune in play: footprint size, bend angle, flutter strength, dew sheen strength, recovery times, matrix factors, scorch rates, site factors, dormancy length.
  • Hear the other sibling's steps in tall grass? The rustle plays for your own steps only.
  • When is "late spring"? Seed heads show from 18 cm in spring and summer until the calendar has a real late-spring window (seasons).

Rejected

  • One trample texture per lawn: replaced by one window round the local player, which covers every lawn near you (town and the hub included) with one texture.
  • The blade's blow as a channel of the trample field: it left a 0.5 s shimmer trail behind the deck. Worked out in the shader as a ring round the deck instead.
  • A hard shake for the blow: the first version shook the grass hard; too much in the user's playtest. It leans out and flutters a little now.
  • Reworking the grass style: the chunky tuft fits the art direction and the budget.
  • A mulch model: mulch is meant to vanish into the turf.
  • Crisp footprint shapes: they'd need denser grass than one tuft per cell, a much bigger call than the look is worth.
Source docs/game/grass.mdEdited 2026-10-03