Files
Sylpheed/docs/re/h3-units-per-frame-measured.md
sylph-decoder d071898cde re: H3 answered -- 2 units per guest frame, and the settle anchor is t~160
The Port asked which of two of my measurements to believe, 55 units/s vs
~150, a factor of 2.7 off one game. Measured on the plate's own declared
ramp, against a pre-registration committed first.

PREDICTED 11 frames at 2 units/frame, 4.4 at the Port's inferred 5, +/-1.
MEASURED 10 labels, and three consecutive gap-free steps of EXACTLY 23,
which is 255*2/22 on the nose. 5 units/frame is excluded by >2x.

Why the splash read as 5: an alpha step is not a clock rate. For a linear
segment d(alpha)/frame = 255*(units/frame)/T. Splash B's quads step 34 with
a declared T=15; the plate steps 23 with a declared T=22; ONE clock, two
steps 1.5x apart. And the Port's intervals start at each quad's first
submission, which on splash A is already alpha=85 -- not the element's t
where alpha=0, and biased by a different amount per element because T
differs. My own splash-quad-timeline.txt published alpha against frame with
no T beside it, which is the column that makes the conversion possible; it
now carries the warning.

The other half: the 'title settled' anchor is ptcopyright reaching full
alpha -- the last build-in element and the ONLY glyph one, which is what a
glyph counter settling means. Calibrated on the plate's own ramp it lands at
t~168 (t~176 at a flat 2.0/label). The Port's candidates are 118 and 160, 42
units apart: this is 8-16 units from 160 and 50-58 from 118. It is 160.

Instrument fact that bounds all of it: labels with zero draws exist, ~1 in 5,
and the clock does NOT advance a fixed amount across them -- across label
5376 the plate moved +82 where three adjacent labels each moved +23. So an
empty label is a real advance, not a logger artefact, and spans that cross
one are approximate. The conclusion rests on the gap-free steps.

Also recorded: the sweep leaves never settle. They translate monotonically
through every label examined and are still moving when the plate arrives, so
'settled' can only mean the build-in elements are done.

NOT answered, and it is what the port actually needs: units per SECOND.
units/s = (units/frame) x (guest fps); this pins the first at 2 and says
nothing about the second, and 2x30 vs 2x60 differ by exactly the ~2 s the
human reported. My own title-plate-delay-measured.md's 2.13 s does not
reconcile with this capture's 20 labels for the same two anchors -- a real
~2.9x disagreement, now localised to frames->seconds rather than
units->frames. Settling it needs the guest's own frame counter, which
neither capture read.
2026-09-01 16:49:08 +00:00

8.0 KiB
Raw Blame History

H3 — the answer: 2 declared units per guest frame. The 5 was an artefact

Status: measured, against the pre-registration committed before the capture was read. Instrument: ⟨capture⟩ — the real game in Canary, per-draw. 2026-09-01.

Answers the Port's BLOCKED.md H3 (auto/port-p6-audio at 6eccfa8), both halves. Neither answer rests on a renderer of ours.


The prediction, and what happened

Counting guest frames from the first frame ptbtn00 is submitted with α > 0 to the first frame it is submitted with α = 255: 11 frames at 2 units/frame, 4.4 at the Port's inferred 5. Accept ±1.

plate first submitted label 5372, α = 0x2E = 46
plate first at α = 255 label 5380
α = 0 by back-extrapolation at 23/label label 5370
ramp span 10 labels

10 against a predicted 11, inside the stated ±1. The prediction held, and 4.4 is excluded by more than a factor of two.

And the local steps are not merely close — they are exact:

label   5372 5373 5374 5375 [5376] 5377 5378 [5379] 5380
alpha     46   69   92  115    —    197  220    —    255
step          +23  +23  +23         (+82)  +23      (+35, clamped)

255 × 2 / 22 = 23.18. Three consecutive steps of exactly 23 is 2.0 declared units per frame on the nose, on the element the play-test complains about, on a screen that is not a splash. ui-keyframe-time-unit.md's law reproduces.

Why the splash read as 5, and why that reading cannot work

The Port derived 5 units/frame from three intervals of my splash timeline. The derivation is sound arithmetic on an unsound premise, and the premise is one I handed them:

An alpha step is not a clock rate. For a linear segment,

Δα per frame = 255 × (units per frame) / T          T = the segment's declared length

so two elements with different T show different Δα at an identical clock. The disc says exactly that, and all three numbers now agree at one clock:

element Δα per frame, measured implied T at 2 units/frame declared T
splash B's six quads 34 15.0 15 — the value ui-keyframe-time-unit.md already records
ptbtn00, the plate 23 22.2 22 (t=214 → 236)

Two screens, two step sizes 1.5× apart, one clock. Reading either step as a rate gives a different answer, which is the whole of the factor of 2.7.

The second defect is the anchor. The Port's intervals start at a quad's first submission, and on splash A the first submission of Q7 is already α = 85 and of Q0 α = 85 — neither is that element's t for α = 0. An interval between two biased onsets is not the interval between two declared times, and the bias is not equal because the two elements have different T.

⚠️ This is mine to have caused. splash-quad-timeline.txt published alpha against frame with no T beside it, which is precisely the column that makes the conversion possible. It now says so.

The other half — the "title settled" anchor is t ≈ 160, not t = 118

The Port asks which of two declared times my operational anchor — glyph counter first reads its no-plate value 154 — corresponds to. The same capture answers it, because the draw stream names which element is which.

ptcopyright is the last element to finish building in, and it is the only glyph element on the screen. A glyph counter settling is that element reaching full alpha:

label    5341 5342 5343 5344 5345 [5346] 5347 5348 5349 5350 → 255 thereafter
alpha      23   57   81  104  139    —    208  231  243  255

Calibrating on the plate's own declared ramp (α=46 ↔ t=217.97, α=255 ↔ t=236, so 2.25 units/label over that stretch):

anchor label implied declared t
ptcopyright reaches α = 255 5350 t ≈ 168 (t ≈ 176 at a flat 2.0/label)
the Port's candidate A t = 118 — 50 to 58 units away
the Port's candidate B t = 160 — 8 to 16 units away

It is candidate B. The margin is not marginal: the two candidates are 42 units apart and the measurement sits within a third of one gap of B and more than a whole gap from A.

⚠️ And the Port's own note says this is the reading under which clock: "shared" collapses. I am reporting what the game does; what that costs the port's model is theirs. It is not evidence against the measurement.

📌 Also from this stream, for whoever defines "settled" next: the sweep leaves never settle. The two off-screen-wide quads translate monotonically through every one of the 100 labels examined and are still moving when the plate arrives. "The title has settled" can only ever mean the build-in elements have finished, never the screen has stopped changing.

The instrument fact that bounds all of this

Labels with zero draws exist, and the animation clock does not advance a fixed amount across them. In 5340…5390 the empty labels are 5346, 5351, 5360, 5367, 5371, 5376, 5379, 5383, 5388 — about one in five.

Across the empty label 5376 the plate's alpha moved +82, where three adjacent occupied labels each moved +23. That is ~3.5 nominal steps for one missing label, not one and not two. So:

  • an empty label is not a logger artefact — the clock really did advance across it, by more than a step; and
  • frame counts that span a gap are approximate, and every count on this page that does so is quoted as such.

This is why the ramp span is quoted as 10 labels and the conclusion rests on the three gap-free steps of exactly 23 rather than on the span alone.

🔴 What is still NOT answered, and it is the part the port actually needs

Units per second is not settled by this, and my own two captures disagree about it by roughly the factor that started this.

units/second = (units/frame) × (guest frames/second). This page pins the first factor at 2. It says nothing about the second, and the second is where the disagreement now lives:

  • the port uses 60 units/s, i.e. 2 × 30 fps;
  • 2 × 60 fps would be 120 units/s, which would put the plate at 1.97 s instead of 3.93 s — and "about two seconds early" is the size of the thing the human reported;
  • this capture produced 6 565 labels in ~241 s ≈ 27.2 labels/s, which is Canary's presentation rate, not the guest's nominal rate, and cannot distinguish a 30 Hz guest running at full speed from a 60 Hz guest running at half.

⚠️ And title-plate-delay-measured.md's 2.13 s does not reconcile with this capture: settled → plate onset is 20 labels ≈ 0.73 s here against 2.13 s there. The two anchors are the same events, so that is a real ~2.9× disagreement between two of my own captures, and it is now localised — not in units→frames, which this page settles, but in frames→seconds. That page's number is a wall-clock duration read off a screenshot stream, which ../agents/TEMPORAL-VERIFICATION.md says not to trust; this page's is a count. I am not withdrawing it on that ground alone, and I am not reconciling them by argument.

The experiment that settles it is to read the guest's own frame counter or vblank rate directly rather than any wall clock — guest memory and CPU state are available and neither of these two captures used them. That is the next iteration's first item.

Reach

One capture, one boot, one title. The 2 units/frame figure now has two independent screens behind it (the splashes' 34-with-T=15 and the plate's 23-with-T=22) and reproduces a law the corpus already held on a third route, so I would expect it to be general. The anchor result is about GP_TITLE build 4 specifically and does not generalise anywhere.

Reproduce

GRACE=1 NOTAP=1 ARM=early FRAMES=20000 MAXDRAWS=400000 \
  tools/re-capture/ui_draw_capture.sh 780 /sylph-home/re/titleplate
# the title arrives ~243 s in; leave the emulator up ~75 s more, then kill it

Series: data/title-plate-ramp.txt.