feat: the boards stop being three products
The owner asked twice why there are still three separate boards. The honest answer, and what this round executes: **it feels like three boards, but not because the scale jumps 20x — because the three packs draw three different Californias, and the loudest difference is that the mountains are four times taller on one of them.** **THE 20x HORIZONTAL SCALE JUMP IS INVISIBLE**, and measuring that collapsed the cost of this whole round. `World.project` is a uniform scale in x/z with no vertical term, and a uniform scale leaves a perspective image identical — so a camera carried across the seam on matched true-metre offsets draws a pixel-identical horizontal frame. 1,919 -> 94 m/unit costs nothing to look at. Rescaling was never the problem. A boot card, a tab strip and a 4.17x vertical deflation were. **THE PAUSE WAS MOSTLY FAKE.** A switch covered the screen for 1,715 ms but only 608 ms blocked the main thread; the page drew 46 of 69 frames with nothing to show, because the outgoing board had already been disposed. `mountCity` now retains it: the incoming board builds BEHIND a live, interactive picture, and `stage.setScene` fires only on completion. Measured across all six directions, three runs each — boot card yes -> **no**, opaque cover 726-1,415 ms -> **0**, blank frames 21-46 -> **exactly 1**, wall clock down 12-29%, blocked main thread down 15-47%. A return to a board already seen links **zero** shader programs and blocks **zero** milliseconds: 298-312 ms of camera flight where it was ~1,600 ms behind a card. Disposal had been throwing away the shader cache too — linkProgram ran 38, 59, 78, 109, 127 across five mounts and never reused one. **The transition is a fog dip, not a crossfade**, through the `setAerialFog` seam built last round. Every both-boards-live crossfade breaks a budget — ca+sf is 2,640,307 triangles against bay-area's 2,600,000 cap — and a fade never lands inside the harness's sample window, which is the "a cap you do not measure is a cap you do not have" failure this repo already argues against. The dip costs zero triangles and zero draw calls, and it hides the 4.17x deflation, the 4,025 m projection disagreement and the vanishing 2 km freeway symbols at once, because all three happen at maximum obscuration. It is also diegetic: a descent through haze. The first dip was wrong and the photograph caught it: collapsing to 6% of board SPAN turned the whole night frame into one flat field — the exact "turns the map off" failure the risk list named. Re-anchored to 70% of camera STAND-OFF, so the coastline survives and only the relief melts. **One ladder, one places list.** 26 authored chapters become 24 rungs sorted descending by STAND-OFF, not altitude — by altitude they interleave badly and altitude cannot tell a low oblique from a high plan. The three-board tab strip is off by default; the left column is now one scrolling list of all 24 rungs under three region headings that does not change when the board does. Only which row is lit changes. Label collisions are resolved in the ladder and never in a pack, so the 29 index-aimed capture guards are untouched. The minimap stops turning through 90 degrees between boards: every board is pinned to a rectangle with California's proportions. **SF and SoCal are not regressed**, and that was the acceptance that mattered: 95.9-98.8% of board pixels are delta-0 against a baseline hash-verified identical to what the live site serves, and every one of the 34-70 surviving pixels per frame is an aircraft or a hull. **A real defect found only by photograph:** `minimap.setMarkers()` had zero call sites. Every marker on every board was gone — the LA studio's door dot, the Bay Area's eight company markers — dropped when the minimap went per-board. Typecheck, tests, budgets and the console were all green with that bug in. Also fixed: two capture presets that lied. `look.mjs`'s `glyph-la` and `glyph-sf` claimed California chapter closeups and returned SoCal and Bay Area frames, because they aimed by chapter index and the indices had moved. Aiming is now by identity, with a guard test. NOT SHIPPED, DELIBERATELY: the pack merge. At Bay density it is 34.04M triangles, 13x the highest budget — dead, not a trade. At SoCal density it is 1.99M and fits today, and the price is San Francisco rendering at 164 m lots instead of 40 m, i.e. SF looking the way SoCal looks now. SF and SoCal carry every marketing still on the site. That is the owner's decision and it is worthless as an argument and decisive as a photograph, so it ships as a measurement artifact with a side-by-side still and is wired into nothing. The four data reconciliations that would make one world honest — one exaggeration rule, roads in metres, one projection centre, one coastline convention — are behind TERA_RECONCILE, default OFF. Tests 1,570 -> 1,651, server 295. All ten budget cells pass, no cap raised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,7 +51,7 @@ things fall out of it that somebody has to pick up.
|
||||
sky layer, and its triangle count did not move by one. The card read 500 MHz of
|
||||
2725 through that run as well.
|
||||
|
||||
### Aerial perspective is wired, and one seam is still coarse
|
||||
### Aerial perspective is wired, and the coarse seam is now a live defect
|
||||
|
||||
Fog now follows camera altitude (`aerialReach` in `engine/atmosphere.ts`, §12.2).
|
||||
`main.ts` recomputes the rig on the controls' `change` event, throttled at 2% of
|
||||
@@ -62,9 +62,32 @@ What that does **not** cover is a camera moved by something other than
|
||||
`OrbitControls`: the drive, actor and aircraft follow-cameras set the pose
|
||||
directly each frame and do not fire `change`. Those poses fall back to the
|
||||
one-hertz clock tick, so a fast descent under a follow camera steps the fog up
|
||||
to four times instead of easing it. It is not visible on the boards that exist —
|
||||
the follow cameras sit low and stay there — but a continuous state-to-city
|
||||
descent would need this on the frame loop rather than on an event.
|
||||
to four times instead of easing it.
|
||||
|
||||
**Promoted from hypothetical to live, 2026-08-23.** This entry used to end "a
|
||||
continuous state-to-city descent *would* need this on the frame loop rather than
|
||||
on an event". That descent is now the thing being built: the state-to-metro
|
||||
transition is a **fog dip** — collapse the outgoing board's `setAerialFog`
|
||||
toward the camera over ~0.3 s, swap in the obscured frame, lift the incoming
|
||||
board's over ~0.5 s — chosen over a crossfade because a crossfade cannot afford
|
||||
the triangles (ca+sf is 2,640,307 against the Bay Area's 2,600,000 cap) and
|
||||
because a dip is the only transition that hides the 4.2x vertical-exaggeration
|
||||
pop, the 4 km projection slide and the vanishing 2 km freeways at once, since
|
||||
all three happen at maximum obscuration.
|
||||
|
||||
That makes fog a **transition mechanism** rather than an ambience setting, and a
|
||||
mechanism driven by an event the descent does not fire is a mechanism that does
|
||||
not run. Two specific consequences:
|
||||
|
||||
- A dip driven from a follow camera — leaving the corridor drive into the Bay
|
||||
Area is the obvious case — gets the one-hertz tick, so a 300 ms collapse gets
|
||||
at most one step. The dip either does not happen or happens as a single jump.
|
||||
- The 2%-of-altitude throttle is a threshold on *altitude*, and a dip changes
|
||||
near/far with the altitude held still. Nothing in the current trigger notices
|
||||
that at all.
|
||||
|
||||
So this moves onto the frame loop, and it moves as part of the transition work
|
||||
rather than after it.
|
||||
|
||||
### The north is authored, and three things in it are thin
|
||||
|
||||
@@ -530,3 +553,183 @@ The complete fix is to clamp against the camera's focus distance rather than the
|
||||
aircraft's, so the glyph collapses toward its authored size whenever the viewer
|
||||
has zoomed in on something near, at any aircraft range. That is a signature change
|
||||
through `glyphScale`, `tick` and their callers.
|
||||
|
||||
The four presets that photograph this clamp used to cover three stand-offs, not
|
||||
four, and two of them were pictures of a different board — see "The capture
|
||||
harness was aiming by position" below. They are now `glyph-board` (the whole
|
||||
state), `glyph-north` (Shasta and the Klamath knot), `glyph-bay` (the Bay Area
|
||||
oblique) and `glyph-fidi` (downtown San Francisco): four aerial stand-offs over
|
||||
three orders of magnitude, all four aimed by `data-view`.
|
||||
|
||||
## The capture harness was aiming by position, and two presets were lying
|
||||
|
||||
Fixed 2026-08-23. Recorded because the *class* of defect is the one this repo
|
||||
keeps paying for, and because two of the three fixes are guards rather than
|
||||
changes.
|
||||
|
||||
**What was wrong.** `scripts/look.mjs`'s `glyph-la` and `glyph-sf` were
|
||||
documented as "two chapter closeups on that board" — California — and were
|
||||
`--click "^LA$"` and `--click "^SF$"`. Chapters 04 and 05 of the state board are
|
||||
not camera poses: `main.ts:4485` matches them against `CALIFORNIA_DESTINATIONS`
|
||||
and calls `switchCity()`, so both presets left California and returned frames
|
||||
byte-comparable to `hero-socal` and `hero-sf`. Two of the four stand-offs the
|
||||
glyph set claimed to cover were duplicates of two others, and nothing failed.
|
||||
|
||||
**What photographing the fix turned up.** California has exactly **two** aerial
|
||||
poses on it. Of its six chapters, one is the whole board, two are doors, and two
|
||||
— `la-sf-us-101` and `la-sf-i-5` — call
|
||||
`requestControlMode("drive")` at `main.ts:4493`, so clicking either lands a chase
|
||||
camera on a freeway rather than a stand-off. "Two chapter closeups on that board"
|
||||
was never available. The glyph ladder is spread across boards instead, which is
|
||||
a better ladder: `glyph-board` → `glyph-north` → `glyph-bay` → `glyph-fidi`.
|
||||
|
||||
**The three things that landed with it.**
|
||||
|
||||
- `scripts/look.mjs` takes `--chapter <data-view> [--expect <short label>]`, so
|
||||
every preset aims at a chapter's **identity** rather than at its position or at
|
||||
the text printed on it. Both of the other two have now produced a photograph of
|
||||
somewhere else in this repo.
|
||||
- `scripts/performance-budget.mjs` gives every scene a **signature chapter** —
|
||||
one `data-view` that exists on that board and on no other — and refuses to
|
||||
become ready until it is on screen; `california-drive` aims at
|
||||
`data-view='la-sf-us-101'` instead of at chapter index 1. `?city=` falls back
|
||||
to the first board rather than failing, silently, so a `bay-area` cell that had
|
||||
quietly measured California would have passed its 2.6M-triangle cap by a factor
|
||||
of six and stayed green through any regression a phone would choke on. A budget
|
||||
that cannot fail.
|
||||
|
||||
**A signature chapter rather than the pressed board tab, and that is a lesson
|
||||
rather than a preference.** It was written against `[data-board]` first and
|
||||
broke inside the hour, because the tab strip is chrome and chrome is precisely
|
||||
what the continuity work is allowed to change. A chapter id is pack data. Every
|
||||
harness in this repo should key on the thing a camera resolves through, not on
|
||||
the thing a designer is free to move: `check-chapter-identity.mjs` demotes tab
|
||||
drift to a printed note for the same reason, and `switch-cost.mjs` detects
|
||||
arrival from `#chapters` rather than from either the tab or the boot card.
|
||||
- `scripts/check-chapter-identity.mjs` + `scripts/fixtures/chapter-identity.json`
|
||||
pin every chapter's `data-view`, position, printed number and short label under
|
||||
every `?city=`, and `src/test/chapterIdentity.test.ts` re-states the pack half
|
||||
of it in `npm test` with three negative cases that are watched failing.
|
||||
|
||||
**The 26 aims that are still index-aimed, and are deliberately left that way.**
|
||||
`scripts/brand-assets/shots.mjs` (21), `films.mjs` (4) and `capture.mjs` (1, the
|
||||
Open Graph card) each point at `#chapters .chapter[N]` and assert a short label.
|
||||
21 of the 26 aim at a board a `?city=` reaches — 12 Bay Area, 6 California, 3
|
||||
SoCal. Re-pointing them means re-shooting the imagery on lumbridgecorp.com, which
|
||||
is an owner decision rather than a refactor, so this round landed the check that
|
||||
*reports* when they would break instead of moving them.
|
||||
`check-chapter-identity.mjs` reads those three files and prints every aim whose
|
||||
label no longer matches the board it points at.
|
||||
|
||||
The label assertion on its own is **not** enough and the reason is in the data:
|
||||
"Whole Board" is chapter 01 of both metro boards and "The Valley" is a different
|
||||
valley on each of them, so a reorder that preserves labels passes all 26 and
|
||||
shoots the wrong place. Identity is `(board, chapter id, position)` together.
|
||||
|
||||
## A board switch is measurable now: `node scripts/switch-cost.mjs`
|
||||
|
||||
The pause between one board and the next was the most-complained-about moment in
|
||||
the product and the one moment nothing measured, because `performance-budget.mjs`
|
||||
waits `warmup-ms` and throws away exactly the window the switch happens in.
|
||||
|
||||
Baseline at `bcac6aa`, desktop 1440x900, `--repeat 3`, medians. `covered` is
|
||||
click-to-`#boot`-hidden; `blocked` is Long Tasks inside that window; `blank` is
|
||||
frames that issued zero WebGL draw calls:
|
||||
|
||||
| direction | covered | opaque | blocked | longest | frames | blank | live |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| california→sf | 1,715 | 1,415 | 608 | 526 | 69 | 46 | 65% |
|
||||
| socal→sf | 1,627 | 1,326 | 523 | 459 | 69 | 46 | 68% |
|
||||
| california→socal | 1,109 | 809 | 400 | 312 | 45 | 22 | 64% |
|
||||
| sf→socal | 1,027 | 726 | 328 | 245 | 44 | 21 | 68% |
|
||||
| sf→california | 1,120 | 820 | 340 | 196 | 49 | 26 | 70% |
|
||||
| socal→california | 1,061 | 761 | 319 | 205 | 47 | 24 | 70% |
|
||||
|
||||
Two things to read off it.
|
||||
|
||||
**Two thirds of every pause is not work.** 64-70% of the cover is a live,
|
||||
animating page with an empty world in it, because the outgoing board is disposed
|
||||
before the incoming one is built. Retaining boards converts a 1.0-1.7 s
|
||||
full-screen modal into a 0.2-0.6 s freeze of a live picture and costs zero new
|
||||
triangles.
|
||||
|
||||
**Cost belongs to the destination, not to the pair.** The two arrivals at the Bay
|
||||
Area differ by 5%, the two at California by 6%, and Bay Area against California is
|
||||
1.6x. Nothing is reused between boards today — `three` refcounts shader programs
|
||||
per material and deletes them at zero, so disposing a board deletes its programs
|
||||
and returning relinks them — and this table is what will show that changing.
|
||||
|
||||
**And the residual is a separate, nameable defect.** The 526 ms task on an
|
||||
arrival at the Bay Area is main-thread instance placement for 83,137 buildings.
|
||||
Retention does not touch it and will make it *more* visible: today it hides
|
||||
behind a full-screen card with a percentage on it, which reads as busy, and
|
||||
afterwards it will freeze a live picture, which reads as a hang. Chunking that
|
||||
placement across frames, or moving it behind the worker, is its own piece of work
|
||||
with its own payoff.
|
||||
|
||||
The command reports `card` and `arrival` beside the durations for exactly that
|
||||
reason. `card=yes` with two thirds of the frames blank is the disposal model;
|
||||
`card=no` with `blank` near zero is retention, and the same command measures
|
||||
both, because arrival is detected from `#chapters` rather than from the card.
|
||||
A single direction run against the in-flight retention build read
|
||||
`card=no`, 1 blank frame of 60, and a 508 ms single task — which is the shape
|
||||
this section predicts: the modal is gone and the hitch is not.
|
||||
|
||||
## The quadtree's stated blocker was the wrong one
|
||||
|
||||
Recorded 2026-08-23 so the next round does not lose a week re-deriving it. The
|
||||
terrain quadtree has been deferred repeatedly on the grounds that the synchronous
|
||||
sampler stands in its way. **It does not, and the code says so in one line.**
|
||||
|
||||
- `elevationAt` has exactly **one** production caller: `world.ts:545`, inside
|
||||
`computeField`. Nothing in the render path samples it. Everything else reads
|
||||
the cached `Field` through `elevationSampled` / `isLandSampled` /
|
||||
`inParkSampled` / `groundAt`.
|
||||
- `world.lattice()` — the thing that hands out flat arrays — has exactly **two**
|
||||
callers, both in `terrain.ts` (282 and 404).
|
||||
- Measured on this box: `elevationAt` is 1.97 µs/call on SF and 11.98 µs on
|
||||
California, so a complete permanently-resident base pyramid (levels 0-2, 15
|
||||
land tiles, **183 ms** on SF, covering the whole board at 370 m cells) makes
|
||||
every point query synchronous by construction — descend-to-finest-resident is
|
||||
integer arithmetic against today's *two* binary searches in `cellIndex`. No
|
||||
caller ever needs an `await`. The base has to be complete because
|
||||
`minimap.ts:407` samples a 160x160 grid across the **whole** board, including
|
||||
ground the camera never looks at; level 2 satisfies it exactly.
|
||||
|
||||
The API a quadtree must replace is therefore one accessor and two call sites. The
|
||||
real blockers, none of which had been written down:
|
||||
|
||||
1. **Placement drift.** Buildings, landmarks, bridges, airports, ports, markers
|
||||
and road ribbons are placed **once** at scene build against `groundAt`. A
|
||||
level-3 (185 m) representation sits p95 119 m / max 453 m from level 5, so if
|
||||
the resident level under a placed object changes after placement it moves by
|
||||
up to **1.15 scene units** on SF — against a lot of 0.42 units. Buildings
|
||||
float or sink. This, not the sampler, is what the deferral was groping at.
|
||||
2. **The shadow caster.** Selected at the shadow map's own texel error a quadtree
|
||||
caster is 826,206 triangles against today's 53,806 — 15x. View-distance
|
||||
weighted it is 1.3x at the wide pose and 2.9-4.2x at the close ones, i.e.
|
||||
+100k to +174k triangles, against 183,824 of headroom on bay-area/mobile and
|
||||
39 free draw calls. The caster must be bounded by the *visible* selection
|
||||
rather than selected independently, because **never raise a cap**.
|
||||
3. **Determinism.** `performance-budget.mjs`'s own comment says "geometry here is
|
||||
deterministic". A streaming tree makes triangles a function of time and camera
|
||||
path. It survives only because the harness runs `reducedMotion: "reduce"` on a
|
||||
planted pose and waits 3,000 ms of warmup — and realistic selections settle in
|
||||
232-830 ms — but that has to become an explicit `settled()` gate the harness
|
||||
waits on, not a hope.
|
||||
|
||||
Two more facts worth keeping. A single statewide lattice at SF fidelity is dead —
|
||||
23.7M points, ~74 s of worker, 142 MB — because `buildAxis` refines per **axis**,
|
||||
so two focus regions at opposite corners refine nearly the whole board. And the
|
||||
board that would demonstrate the mechanism paying for itself is **SoCal**, not
|
||||
SF: 48.4% of SoCal's lattice is the per-axis cross's arms (64,677 of 133,566)
|
||||
against SF's 31.0%, and SF is the one board where a single hand-drawn rectangle
|
||||
is exactly right — its 52 districts union to 9.6% of the board and its one
|
||||
`focusRegion` box is 9.2%.
|
||||
|
||||
**And today's SF board is not error-bounded, which nobody had noticed.**
|
||||
Cell-centre height error against the analytic truth is p99 12.0 m / max 64.9 m in
|
||||
the fine region and p99 29.1 m / **max 459.3 m** in the coarse one — 27.2 px at
|
||||
the default whole-board pose, out in the bay. The board is cheap because the pack
|
||||
declares 90% of it unimportant. That is a defensible trade and it is not the same
|
||||
thing as being right.
|
||||
|
||||
Reference in New Issue
Block a user