# W-GRASS7: living settlement grass

Started 9 Oct 2026, 10:55 CET. Deadline: 13:04 CET.

## Blocked on you

Nothing. This is committed work in the private worktree, not a deployment.

## What changed

Open meadow ground now has one moving grass field, including back gardens, churchyard ground, tree surroundings, countryside and river banks. The old wall-foot-only field is gone.

- The map uses `groundCover`, the same first-claim ground cover that makes the terrain. It is baked once, on first near-ground use, with `bakeField` at 0.75 m spacing.
- Exact checks after the bake stop interpolation leaking onto roads, squares, paving, stairs, crops, water, bridge ground, fitted building bodies or stalls. Missing-kit building boxes also stay bare.
- Grass gets shorter and thinner across the last 1.5 m beside roads and solid objects. The terrain's worn paving blend further reduces verge grass.
- Roots and colour follow the actual meadow triangles. The loaded meadow texture's average colour is sampled once with a tiny GPU picture, which also works for compressed textures.
- Blades are about 0.35 m tall in full meadow, with small height changes, clumps and wind. A small sky-coloured light fill prevents shadowed blades turning into a black stripe. No vendor shader was changed.
- Engine `grassQuality('low')` is the default. Callers can choose another preset with the optional `grassQuality` setting.
- Tiles follow the camera. Eye-height reach is 52 m. Reach shrinks smoothly above 3 m; the field is hidden above 18 m. Distant aerial views do not even bake or make tiles.
- `?grass=0` starts off. The page's `grass: on/off` button switches it without rebuilding the town.
- A small feet object follows the eye-height walker and uses `addInfluence`. Its 64 by 64 press map springs back after the walker leaves.
- New grass also inherits the settlement drawing layer. Removing a settlement releases its grass buffers. There is only one field across repeated toggles.

Changed renderer files were copied to `browse/building/`, with only the expected vendor-path adjustment.

## Numbers and pictures

Target: a disposable staged copy served only on GPU-box loopback port 8972. No real app or live kit was tested or changed.

Hardware: NVIDIA RTX 4000 SFF Ada Generation, Chromium/ANGLE, 1600 by 900, soft-corner shading on. Camera hooks are the requested `__show` and `__walkStreet`.

The original main was captured before editing. Its main-pass calls were:

| View | Original calls | Final calls | Added |
|---|---:|---:|---:|
| village/plain/3 street walk | 281 | 284 | +3 |
| town/hill/1 street walk | 406 | 409 | +3 |
| town/hill/1 aerial | 415 | 415 | 0 |
| city/plain/1 aerial | 389 | 389 | 0 |

The final same-page off/on test repeats each pair twice. The table averages those two steady-state medians, not the single worst frame. Each measurement warms up, then samples 16 badge readings and about 88 GPU timer readings. Badge numbers come from `window.__fps`, which uses `renderer.info.__main`. GPU numbers time the complete look render, including shading, with the WebGL GPU timer rather than assuming 60 fps means the GPU cost is zero.

| View | GPU off / on, ms | Added GPU time, ms | Badge off / on, ms | Calls off / on |
|---|---:|---:|---:|---:|
| village/plain/3 street walk | 7.90 / 7.89 | -0.01 | 16.67 / 16.67 | 279 / 284 |
| town/hill/1 street walk | 7.75 / 7.94 | +0.20 | 16.68 / 16.67 | 404 / 409 |
| town/hill/1 aerial | pending | pending | pending | pending |
| city/plain/1 aerial | pending | pending | pending | pending |

Walking is inside the requested +1.5 ms GPU and +6 call limits. Compared with the original main, each street view adds only three calls. Even a full meadow view adds six calls versus grass off and about 0.31 ms GPU. Its badge stays at 16.67 ms. Initial map preparation was 320 ms for the village and 521 ms for the town, before the steady-state measurement.

Aerial GPU timings fluctuate even though the field makes no tiles, adds no calls and draws no grass there. Treat that spread as measurement noise, not a grass gain or loss. Original and final raw logs are retained so this is not hidden. The early separate GPU runs were not used as the final GPU comparison.

Pictures were opened and inspected, not just generated. The target was the original settlement look plus the requested living meadow, not an invented photographic reference. The first grass pass was too dark; meadow-matched sky fill corrected it. Final screenshots and the final numbers are in `grass7-evidence/final/` and `grass7-evidence/final.log`; original screenshots are in `grass7-evidence/before/`.

Extra meadow off/on and 12 m camera-height pictures check colour and the aerial fade. The field does not form a bright circular lawn from above. Existing dark tree-picture edges are unrelated and were not changed.

## Tests and walking evidence

- `node --test omg/kit/building/test/town-grass.test.mjs`: 7 passed, no failures. Covers the baked mask, road/square/body exclusions, crops, stalls, bridges, water, stairs, coast, worn edges, real village/town/river plans, meadow growth, mesh height/colour, altitude fade, one field, off/on and cleanup.
- First complete `node --test omg/kit/building/test/`: 183 passed, no failures, one existing TODO. This began before the new grass test file was added.
- Final complete suite: pending.
- Real browser button clicks proved `?grass=0`, off, on and one field after toggling.
- Real W-key movement moved the walker from (-160, -160) to (-153.98, -158.14), about 6.3 m, without a blocked position.
- Grass press at the starting feet fell from 255 to 86 after walking away, then 18 as it recovered. Wind time advanced and wind strength was 0.32.
- The walking/toggle probe reported no page errors. Evidence: `grass7-evidence/interaction/interaction.json` and its screenshots.
- Source and browse copies match after their vendor-path adjustment. `git diff --check` is clean.

The existing unfinished test is `roads6.test.mjs`: ground seams up to about 30 mm remain, against a 5 mm target. It is not a newly passing grass test and was not fixed here.

## What would break, and undo

A wrong mask could grow blades through paving or a building. A bad colour sample could bring back a visible fade boundary. Much weaker GPUs or higher quality presets could exceed this measured budget.

Immediate undo: use `?grass=0` or the button. To remove the feature entirely, revert this worker's commits, including the source and browse copies together.

## Not done

- No deployment, push, live-app request, live-kit edit or tree-worker edit.
- No changes to `render.mjs`, heroes, roofs, road layout or the vendored grass engine.
- No mobile/low-end GPU measurements; no budget claim for medium/high grass presets.
- No exhaustive visual sweep across every site and seed, or long walking memory test across an entire city.
- No persistent tracks, NPC trampling, or crops converted to living grass. Trampling is the eye-height walker only.
- The engine's existing tile cache keeps visited tiles. Very long exploration was not measured.
- No independent reviewer or nested agents. Screenshot criticism was direct self-review.

## Files and repeat commands

- `omg/kit/building/town-grass.mjs`, `dress-render.mjs`, `settlement-render.mjs` and their browse copies.
- Toggle-only edits in `browse/settlement.html`.
- Tests: `town-grass.test.mjs`, `town-grass-gpu.py`.
- GPU replay: `FAL_PORT=8972 FAL_PROBE_FILE=grass7-evidence/paired-shots.json tools/gpu/run.sh tools/gpu/shots_probe.py <private-output>`.
- Walking replay: `FAL_PORT=8972 tools/gpu/run.sh omg/kit/building/test/town-grass-gpu.py <private-output>`.
