Lane B R28 (v5.1): the awning fabric ripple — KILLED with the measurement

Ledger #2. Built it, proved it worked, measured what it was worth, killed it.
A 10x-over-driven billow — 1.4m of flap on a 0.14m canopy — cannot move 1% of
the frame. It is not the gain, it is the geometry. Nothing ships: buildings.js
and wind.js are byte-identical to HEAD.

IT WAS BUILT, AND IT WORKED. Split the stall canopies out of the rigid awning
InstancedMesh (own mesh, own subdivided box — a 4-corner box has nothing to
ripple), gave only them a cloth billow riding R17's wind.js uniforms: sin(pi u)*
sin(pi v) pin (zero at the four posts by construction), two crossed travelling
waves, per-instance phase so forty stalls don't breathe in unison. Proven by
rendered-pixel hashes, not by eye:
  cloth displaces (calm→gusty)        40f59eaf → d6d3e1f1  moves
  back to calm                        40f59eaf              identical
  uWindTime=99 at amp 0               40f59eaf              identical
  ?classic=1, pre- vs post-build      453c0154 → 453c0154   BYTE-IDENTICAL
  rest of town rides wind? (hidden)   76c8b518 → 76c8b518   still
  control: cloth back                 frame moves again
Cost at market_square, true A/B: +0 draws (98→98) / +2,160 tris. +0, not the +1
my own comment claimed — the square's chunks hold only stalls, so the rigid list
empties and makeInstanced returns null; the cloth mesh replaces it.

AND IT IS WORTH NOTHING (amp is the same multiplier as gain):
  0.02m  CALM, the default weather amp        0.05% of frame
  0.14m  STORM, the most a player ever sees   0.21%
  0.35m  2.5x over-driven                     0.33%
  0.70m  5x, absurd on a 0.14m slab           0.49%
  1.40m  10x, flapping a body-length          0.77%
At the player's actual browsing pose, storm-force: 0.00%, max delta 1. Not one
pixel.

THE REASON IS PHYSICAL — the same physics that killed the verandah in R17. A
canopy is a horizontal sheet at y2.2 seen by a 1.7m player: edge-on or from
underneath, so vertical displacement runs almost parallel to the view ray and
makes almost no pixels. You cannot see a flat horizontal sheet flex vertically
while standing under it. No gain fixes it; 10x buys 0.77% and stops reading as
cloth.

WHAT WOULD WORK (for whoever reads this in round 35): the motion must be
perpendicular to the view — VERTICAL cloth: hanging stall skirts, a flag, a
banner, an awning valance. That is an ASSET (Lane E), not a shader. B's
applyWind/applyFabricWind seam is ready the day such a mesh exists; the shader
was never the missing piece. Ledger #2 leaves the backlog forever.

TWO VACUOUS MEASUREMENTS CAUGHT ON THE WAY. (1) `!!material.onBeforeCompile` to
prove the rigid verandah untouched — every THREE.Material inherits a prototype
no-op, so it is ALWAYS truthy; the assert could never go red. Replaced with
hasOwnProperty + a behavioural hide-and-hash control. (2) A gain sweep reporting
an identical 0.21% at gains 0.4→4.0 because I never stored the shader ref, so
uFabricGain never moved — four identical numbers across a 10x range is a sweep
that proves nothing. The constancy, not the value, gave it away. Both would have
passed as green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-17 17:29:39 +10:00
parent afad68b114
commit 999c9d653f

View File

@ -1,5 +1,72 @@
# LANE B — NOTES (measured)
## Round 28 (v5.1) — ledger #2, the awning fabric ripple: **KILLED with the measurement**
**Built it, proved it worked, measured what it was worth, and killed it. A 10×-over-driven billow —
1.4 m of flap on a 0.14 m canopy — cannot move 1% of the frame. It is not the gain; it is the
geometry. The backlog item is closed permanently, and the reason names what *would* work.**
R17 shipped tree sway and parked this with a physical reason: a posted verandah is a structural roof
and does not flutter. R28 asked for the half R17 left — the stalls' striped canopies really are cloth,
and they shared the rigid awning InstancedMesh, so neither could move without the other. That part was
all true, and the split worked. The cloth just turned out to be invisible.
### It was built, and it worked
Split the canopies out of the rigid mesh (own `InstancedMesh`, own subdivided box — a 4-corner box has
nothing to ripple), gave only them a cloth billow riding R17's existing `wind.js` uniforms: a
`sin(πu)·sin(πv)` pin (zero at the four corner posts by construction, peak mid-panel), two crossed
travelling waves, per-instance phase off world position so forty stalls don't breathe in unison.
Proven by **rendered-pixel hashes**, not by eye:
| proof | result |
|---|---|
| the cloth actually displaces (calm vs gusty) | `40f59eaf``d6d3e1f1`**moves** |
| returning to calm reproduces the calm frame | `40f59eaf`**identical** |
| `uWindTime = 99` at amp 0 | `40f59eaf`**identical** (amp 0 ⇒ `+= …*0.0`, the R17 pattern) |
| **`?classic=1`, same fixed pose, pre- vs post-change build** | **`453c0154``453c0154` — byte-identical** |
| rigid verandahs / rest of town ride the wind? | cloth+trees hidden: `76c8b518``76c8b518`**still** |
| …and the control proves I hid the right thing | cloth restored: frame **moves** again |
Cost at `market_square`, true A/B: **+0 draws** (98 → 98) / **+2,160 tris**. +0, not the +1 I'd written
in my own comment — the square's chunks hold *only* stalls, so the rigid list empties, `makeInstanced`
returns null, and the cloth mesh replaces it rather than adding to it.
### And it is worth nothing — the sweep (amp is the same multiplier as gain)
| peak billow | frame changed |
|---|---|
| **0.02 m — CALM, the default weather amp** | **0.05%** |
| **0.14 m — STORM, the most any player ever sees** | **0.21%** |
| 0.35 m (2.5× over-driven) | 0.33% |
| 0.70 m (5×, absurd on a 0.14 m slab) | 0.49% |
| **1.40 m (10×, a canopy flapping a body-length)** | **0.77%** |
At the player's actual browsing pose, storm-force: **0.00%, max channel delta 1**. Not one pixel.
**The reason is physical, and it is the same physics that killed the verandah in R17.** A canopy is a
horizontal sheet at y 2.2 viewed by a 1.7 m player: you see it edge-on or from underneath, so a
*vertical* displacement runs almost parallel to the view ray and produces almost no pixel change. You
cannot see a flat horizontal sheet flex vertically while standing under it. No gain fixes that — 10×
buys 0.77%, and anything that large stops reading as cloth and starts reading as a trampoline.
**What would work, if wind in the market is ever wanted (for whoever reads this in round 35):** the
motion has to be *perpendicular* to the view, which means **vertical cloth** — hanging stall skirts, a
flag, a banner, an awning valance. That is an **asset** (Lane E), not a shader. B's `applyWind` /
`applyFabricWind` seam is ready for it the day such a mesh exists; the shader was never the missing
piece. **Ledger #2 leaves the backlog forever.**
### Kept from the round
Nothing shipped: `buildings.js` and `wind.js` are byte-identical to HEAD. Shipping an invisible ripple
would have cost 2,160 tris and a second draw path in the covenant town to move 0.05% of a calm frame.
*The R17 rule was "ships or dies". It died — and it died with numbers, which is the only way an item
this old stops coming back.*
**Two vacuous measurements caught on the way (the epoch's own disease, still catching me):** (1) a
`!!material.onBeforeCompile` check to prove the rigid verandah was untouched — every `THREE.Material`
inherits a prototype no-op, so it is **always** truthy; the assert could never go red, and I replaced it
with `hasOwnProperty` plus a behavioural hide-and-hash control. (2) a gain sweep that reported the
identical 0.21% at gains 0.4→4.0 because I never stored the shader ref, so `uFabricGain` never moved —
**four identical numbers across a 10× range is a sweep that proves nothing**, and it was the constancy,
not the value, that gave it away. Both would have passed as green.
---
## Round 27 wave 4 — `poseForShopFront` shipped · **and every real town's shops face away from the street**
**The micro-task is done and verified. In doing it I found why F photographed empty road: it is not