1
0
This repository has been archived on 2026-08-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tera/TODO.md
T
karti b7f5c41da5 feat(brand): a shot can take a body, so the last hand-made stills are generated
Three pictures on /arena — the chase camera behind the EV, the crow in flight, the
aircraft over the board — were the only imagery on the site with no generator
script anywhere in either repo. They were made by hand, and so they were a single
daylight frame served to dark-mode readers too, with no pair to choose from.

`shots.mjs` gains a `play` capability beside `aim`: a shot names a control mode,
the script clicks the app's own mode dock, and only then shoots — with the HUD
deliberately left in frame, because on these three the body and its readout ARE
the subject. `#mode-dock` and `#play-hud` are in FURNITURE.BARE rather than
CLUTTER for that reason, and there is a comment at the `hide()` call so a later
tidy-up does not crop the subject out.

Three guards, each verified by deliberately making it fire:

  - the mode must be OFFERED, not merely present — asking for `drive` on the Bay
    Area board now says `"drive" is not offered here — the dock is showing
    [overview, actor]`;
  - `aria-pressed="true"` AND `#play-hud` unhidden, the same pair
    `performance-budget.mjs` waits on;
  - the HUD's own body chip, so asking for `Explore` when an anonymous visitor
    gets a crow says `the play HUD says "Crow", not "Explore"`.

The body guard runs twice, at the click and again at the shutter, because a
completed route or a bound ends a mode mid-wait and hands back an orbit wide shot
under the right filename.

`untilProgress` exists because the first delivery drifted: `run: 9000` gave 55% of
the leg in a 1x preview and 69% in the 2x render — a different stretch of road,
with a town's edge across the top of the frame. Road traffic runs from page load,
so a duration is really "boot plus a bit". Waiting on the app's own route
percentage lands preview and delivery on the same frame; overshoot throws rather
than shooting. The crow and the aircraft are built inert, so a duration is exact
for them and only the car needed the gate — measured, not assumed.

The run log now echoes the HUD (`[Drive] 56 mph · US-101 | assisted · 55% ·
chase`), because "shot drive-101-day" is equally true of a parked car, a finished
route and a correct frame.

The crow moved from the state board to the Bay Area. On California a one-metre
bird stands against ground drawn at 1,919 m to the unit with an oak the size of a
hill behind it, and the night frame would have been a black rectangle with a bird
in it. `crow-nav-v1`'s `source` said "Tera / California" and that was wrong on the
environment's own terms as well — `crowNav.ts` runs a ±120 m box with no terrain
at all.

One frame is honestly weak and is recorded rather than dressed up: `drive-101`
at night. From behind, on an unlit corridor, the EV is two tail lamps and a roof
strip. Four hours were shot between 21:15 and 20:05 and the corridor's ground is
equally black at all of them, so the hour is not the lever; both real fixes are in
the engine and are in TODO.md. It is still the honest night frame.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 13:30:23 -07:00

194 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Open work on Tera
Short, and only things that are decided but not done. Anything speculative belongs
in an issue, not here.
## One command re-shoots the imagery: `npm run refresh`
```sh
npm run refresh # build, stills, cards, films, both manifests
npm run refresh -- --dry-run # what it would shoot; no browser, no build
npm run refresh -- --stills-only # …--cards-only, --films-only; they compose
npm run refresh -- --rough # films at 24 frames, for looking at
npm run refresh -- --skip-build # you just built; do not build again
```
`scripts/brand-assets/refresh.mjs` exists because re-shooting used to be a
*procedure* — build first, because the scripts photograph `dist/` and not `src/`;
then three commands in an order that matters; then check the run actually got the
GPU. A procedure that lives in somebody's head gets half-run, and the evidence was
in this file: the stills were re-shot at one commit and the films at another two
weeks older, and nothing said so until a human compared two files.
It prints a diff of the deliverables at the end — new, changed with the size delta,
byte-identical — and it **fails loudly and specifically** on the three failures
that otherwise produce confident wrong output:
- **SwiftShader.** Software rendering makes the same pictures fifteen times slower
and reports success. Any child that prints a software renderer stops the run.
- **A chapter `expect` guard firing.** That means a pack was reordered under the
shot list, so re-running cannot fix it. `node scripts/brand-assets/shots.mjs
--list` prints what the shot list believes.
- **A `play` guard firing.** A shot that takes a body did not get the one it asked
for — see the section below. Also not a flake, and also not fixed by re-running.
Two things it does **not** do, on purpose: it does not commit, and it does not
re-stamp the manifests afterwards. Commit tera first, then
`shots.mjs --manifest-only` and `films.mjs --manifest-only`, so the manifests name
a clean sha rather than a dirty tree.
## A shot can take a body now — `play` in `shots.mjs`
Three stills are pictures of an agent rather than of a place: `drive-101`
(chase camera behind the EV on US-101), `crow-nav` (the crow over the Bay Area
board) and `california-flight` (the aircraft on the corridor). They replace three
hand-made screenshots in `lumbridge-v4/apps/web/public/arena/`, which were the
last imagery on the site no script could reproduce — and so also the last that
nobody could re-shoot when the engine moved. All three still showed a left column
reading "Explore as your crow →", a "FULL VIEW" badge no visitor to the site ever
sees, and one daylight frame served to dark-mode readers as well.
`play` drives the mode dock, which is the same class of input `aim` is: no back
door, just the control a reader uses. `#mode-dock` and `#play-hud` stay **in** these
three frames deliberately — they are what the picture is of — while remaining in
`FURNITURE.BARE` for the share cards.
Three guards, all of them tested by making them fire, because every way this goes
wrong renders a plausible-looking frame with the right filename:
- the dock button must be *offered*, not merely present — the EV and the aircraft
exist on the California board alone, and driving also needs US-101 or I-5 to be
the current chapter, so elsewhere the button is `hidden` and clicking it is a
no-op that leaves an orbit wide shot;
- `aria-pressed` **and** `#play-hud`, exactly as `performance-budget.mjs` does —
`transitionControlMode` substitutes a fallback for a request it will not honour;
- the HUD's own chip names the body ("Drive", "Crow", "Flight"), checked at the
click *and again at the shutter*, because a route that completes or a bound that
is hit ends the mode mid-wait.
`refresh.mjs` surfaces all three as their own headline, next to SwiftShader and the
chapter guard.
**`untilProgress`, not a duration, for anything that was already moving.** The crow
and the aircraft are built inert, so *n* ms after the dock hands them over is the
same place every time — measured identical at 1x and 2x. Road traffic is not: it
runs from page load, so a duration is really "boot plus a bit", and the same
`run: 9000` put the EV at 55% of the leg in a 1x preview and 69% in the 2x render,
which is a different stretch of road with the edge of a town across the top of the
frame. `untilProgress` waits on the app's own route percentage instead, so preview
and delivery agree. Overshoot throws rather than shooting.
### `drive-101`'s night frame is the weak one, and it is the product not the capture
Canvas-region mean luminance, 1600w: `crow-nav` 23.2, `california-flight` 13.6,
`drive-101` **7.8** — against `fidi` 16.0, `golden-gate` 8.2 and the `bay-bridge`
frame this file already calls out, 8.3. Nothing in it needs a lift to be legible —
the road, the markings, the panel, the HUD and the dock all read at native
exposure — but the *subject* nearly disappears: seen from behind on an unlit
corridor the EV is two tail lamps and a roof strip, and the hour is not the lever.
Shot at 21:15, 20:45, 20:20 and 20:05 (sun 15.7° to 3.1°) the corridor's ground
is equally black, and by 20:05 the plan view has flipped to its daylight styling
while the scene has not, which is worse. The two candidate fixes are both in the
engine: give the EV a headlamp spill on the road ahead, or let the corridor's
terrain keep some moonlight the way the city boards' ground does.
The crow moved to the Bay Area board for the same reason and it worked: over a city
the night frame is the *better* of its pair. The hand-made original was on the state
board, where a one-metre bird stands in front of ground drawn at 1,919 m to the unit
with an oak the size of a hill behind it.
## Re-shoot the time-lapse films
The 28 product stills and both share cards were re-shot on 2026-08-22 at the
airports-and-bridges commit and are current. **The films were not** — they were
last shot on 2026-08-07 at `9c9e78f`. `films.ts` in lumbridge-v4 records the tera
commit each reel was shot at, so a stale reel is visually stale and no caption edit
fixes it — check that commit against tera HEAD before assuming a reel is current.
What the reels predate: ACES tone mapping and the runtime PMREM environment rig; a
sea that reflects, with a swell normal map, Fresnel and sun glitter, in place of a
flat `MeshLambertMaterial` that had no specular term at all; a world-space sky dome
replacing the screen-space gradient, which at dusk painted the sunset band along the
*bottom* of the picture, under the board; terrain that casts shadows; a California
board with the Basin and Range, Death Valley, the Salton Sea and San Diego on it;
SFO, LAX, both bridges and a freeway that reads as a road; aircraft that move,
carrying registration and type; a rebuilt LA studio; the studio device hardware; and
a new UI chrome with a first-run flow.
```sh
npm run refresh -- --films-only --rough # rough cut first: ~20 s a reel, and it is
# the cheapest way to find out a camera is
# pointed at the wrong thing
npm run refresh -- --films-only # ~90 s per 180-frame film on the Radeon
```
Worth knowing before starting (the rest is in `~/.claude/skills/tera-capture`):
- Check the run prints the Radeon and not SwiftShader. The software path is ~15x
slower — 22 minutes a film rather than 90 seconds — and it reports success.
- `hero-soma` is a different animal: `chrome: "bare"`, midnight to midnight, and a
two-stage encode (a CRF 18 master, then 1440w CRF 28 and 960w CRF 30 delivered).
- **The hero camera is worth reconsidering.** Whole-board was tried and rejected
because the edge of the terrain plate showed against the sky. That seam no longer
exists: the sea now runs eighteen board spans and ends past the fog's far plane.
- After tuning the hero, run `npm run check:hero` in lumbridge-v4.
- `#onboarding-host` is in `FURNITURE.CLUTTER` now, and `#mode-dock` and
`#play-hud` are in `FURNITURE.BARE`. Every *new* overlay has to be added to one
of those two lists on the day it lands, because a frame with a stray card in it
still renders and still looks deliberate. The mode dock proved it: it shipped
inside the tera share card for a fortnight and nobody noticed.
- **The two capture scripts both serve on port 5210.** Two of them at once is
`EADDRINUSE` a minute into a run, so do not start a film while a stills pass is
going — or give one of them `--site`-style isolation first.
- Commit tera first, then re-run, so the manifest records a clean sha.
## The Bay Area board drops a frame in twenty, on desktop only
`bay-area.desktop.p95FrameIntervalMs` carries a **33.4 ms allowance and that is a
recorded defect, not a target.** The board renders a median frame in 16.7 ms and
drops roughly one frame in twenty: p50 16.7, p95 33.3, and 443456 frame samples
in a window where every other cell returns 480.
It is desktop-only, and it is **not fill rate**: the mobile cell runs the *same*
2.26 M triangles at a comparable pixel count — 1.32 MP against desktop's 1.30 —
and holds 16.7 ms flat. The obvious suspect is the shadow map, which `stage.ts`
sizes **2048 on desktop and 1024 on handheld**, over what is now the heaviest
shadow-casting scene in the product.
**It is not a regression, and this was checked rather than assumed.** Measured at
the commit before the airports and bridges landed, with the same harness:
p95 33.3, p50 16.7, 443 samples, **2,771,606 triangles**. After that work:
p95 33.3, p50 16.7, 456 samples, **2,265,056 triangles** — the board got
506,550 triangles *lighter* while gaining SFO, both bridges and a surfaced
freeway. The stutter was simply invisible until `bay-area` became a measured
cell, which it had never been.
The allowance is there so the cell still guards the numbers that are healthy —
triangles, draw calls, and the mobile frame time — rather than sitting
permanently red and therefore permanently ignored. **Fix the stutter and put the
cap back to 16.7.** Start with the desktop shadow-map size and the shadow
frustum over the SF board; a 2048 map over 2.26 M triangles of casters is the
first thing to rule in or out.
## The aeroplane glyph is still larger than the Golden Gate
`GLYPH_MAX_SCALE` in `src/engine/flights.ts` is 52 and that is a mitigation, not
a cure. The glyph has a screen-space *floor* — never smaller than legible — which
scales by the distance to the **aircraft**, when what makes it look wrong is how
far the camera is from **what it is looking at**. At a whole-board pose those are
the same thing; at the Golden Gate chapter the bridge is two units from the camera
and the traffic is two thousand, so the floor fires hard on the aeroplane and not
at all on the bridge.
A ceiling of 52 was chosen because the raw scale at 1,160 units — the far end of
the orbit over the California corridor — is 51.0 at a 60-degree field of view, so
anything lower shrinks aeroplanes at a pose people actually use. (26 was tried
first and put the glyph at 0.0123 of the frame, against the 0.012 at which
`flights.ts` says the wings stop resolving.) 52 takes the worst case from about
two and a half times the bridge's main span down to about one and a half.
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.