Files
Sylpheed/docs/re/splash-interpolates-every-frame.md
sylph-decoder e6f921d442 re: the game interpolates every frame, and its splash is ALSO mostly frozen
Answers question 1 of the 2026-09-02 play-test: interpolate or hold. It
interpolates, piecewise-linearly, evaluated once per present at one unit
per present.

palogo_sqex_eff gives 28 distinct alphas over 28 consecutive presents,
changing on 26 of 27 adjacent pairs. It is not one slope, and the disc says
why: that element declares 0:a=0 -> 15:a=255 -> 30:a=212 -> 45:a=0, three
segments with three gradients. Predicted -2.87 and -14.13 per unit against
measured modal steps of -3 (x6) and -14 (x9). The declared keyframes
predict the per-frame steps, which makes this a prediction rather than a
description.

So the port must lerp: hold-to-next-key would emit 3 states where the game
emits 28. And "eased" is the wrong description -- there is no easing
function, the envelope only looks eased because consecutive declared
segments have different gradients.

Alpha lives in the per-vertex k_8_8_8_8 colour, rewritten into a fresh
vertex buffer every frame. Not a PS constant (ps_c[n=0] on 1048/1048), not
a blend factor, not a texture swap.

🔴 And the game's splash is ALSO mostly frozen. Measured with the same
statistic the play-test used on the port: the game moves 21.2% and 27.8%
against the port's 16.4%, and its longest frozen run is 3.34 s against the
port's 3.20 s. The publisher declares 205 of 255 units -- 80% -- as a flat
hold at full opacity.

That refutes the play-test's second clause. "A fade does not hold one
picture for 3.20 s" -- this one does, for 3.34 s. Its first clause stands:
26 states for a 45-unit build-in is too few, the game gives 49 and 51.

The deficit is the state count, not the freeze: ~100 against 26. Acting on
the second clause would have sent the port to remove the one part of its
splash that is already right.

Not answered: which function does it. This is the behaviour; the
image-side half of question 1 is still open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
2026-09-02 15:29:34 +00:00

4.9 KiB
Raw Blame History

The game interpolates piecewise-linearly, every frame — and its splash is also mostly frozen

Status: measured, and the declared keyframes predict the measured steps. 2026-09-02. Instrument: ⟨capture⟩ — per-frame vertex alpha off the guest's own vertex buffer; ⟨disc⟩ for the keyframes that predict it. Series: data/splash-per-frame-alpha-series.txt.

Answers question 1 of ../agents/PLAYTEST-2026-09-02.md"what does it do BETWEEN keyframes — interpolate, or hold to the next key? That single answer decides whether the port should lerp at all."


1 — it interpolates. Every frame. Piecewise-linearly.

palogo_sqex_eff, 28 consecutive presents, 28 distinct alphas, changing on 26 of 27 adjacent pairs:

34  85 204 221 238 254 249 246 243 237 232 229 226 223 214 211 197 169 141
127 113  98  84  70  56  42  28  14

It is not one slope, and the disc says why. That element declares 0:a=0 → 15:a=255 → 30:a=212 → 45:a=0, which is three segments with three different gradients. Predicted against measured, per unit:

declared segment predicted Δα/unit measured modal step
15 → 30 (255 → 212) 2.87 3 ×6
30 → 45 (212 → 0) 14.13 14 ×9

The declared keyframes predict the per-frame steps. That is the whole answer: the game evaluates a linear interpolation between the two bracketing keyframes, once per present, at one unit per present.

The port must lerp. Hold-to-next-key would emit 3 states where the game emits 28.

⚠️ And "eased" is the wrong description, which matters because a port that believes it will fit a curve. There is no easing function — the envelope looks eased only because consecutive declared segments have different gradients. Every segment is straight.

2 — where the alpha lives, so it can be watched

The per-vertex k_8_8_8_8 colour, in a vertex buffer the guest rewrites into a fresh address every frame. Ruled out by the same captures:

  • not a PS constantps_c[n=0] on 1 048 / 1 048 splash draws;
  • not a blend factor — the blend register is constant across the whole splash;
  • not a texture swap — one texture is bound throughout.

3 — 🔴 the game's splash is ALSO mostly frozen, and the port's 3.2 s hold is CORRECT

Measured with the same statistic the play-test applied to the port:

game publisher game developer port (both)
moving 21.2 % 27.8 % 16.4 %
longest frozen run 3.34 s 2.50 s 3.20 s
distinct states 49 51 26 total

📌 The play-test's inference does not hold, though its observation does. It reads "a fade does not hold one picture for 3.20 s" as proof of breakage. The game holds one picture for 3.34 s. The publisher's declared timeline is 0:a=0 → 15:a=0 → 30:a=255 → 235:a=255 → …, i.e. 205 of 255 units — 80 % — is a flat hold at full opacity. A splash that is static most of the time is what the disc describes.

🔴 So the deficit is not the freeze. It is the state count: ~100 against 26. The game changes alpha on every present during a ramp — 15 distinct values for a 15-unit ramp — and the port produces roughly a quarter of that across both splashes. Fixing the hold would make it worse; the ramps are where the states are missing.

What the port should do

  1. Lerp between bracketing keyframes, evaluated once per frame — not hold-to-next-key, and not an easing function.
  2. Keep the long hold. ~3.3 s of genuinely identical frames is correct.
  3. Expect ~15 distinct alphas per 15-unit ramp, ~100 across both splashes.
  4. Alpha is a per-quad scalar, uniform across the quad's four vertices.

Refutation attempt, recorded per the adversarial duty

Target: the play-test's "A 45-unit build-in cannot be drawn in 26 states, and a fade does not hold one picture for 3.20 s."

Result: the first clause SURVIVES, the second is REFUTED. 26 states for a 45-unit build-in is indeed too few — the game gives 49 and 51. But the game holds one picture for 3.34 s, longer than the port's 3.20 s, and the disc declares 80 % of the publisher splash as a flat hold. Recorded because acting on the second clause would have sent the port to remove the one part of its splash that is right.

Reach

⟨capture⟩, one boot, both splashes, English locale. The interpolation law is checked against ⟨disc⟩ keyframes on one element with three gradients, which is what makes it a prediction rather than a description — it has not been checked on the title or the menus, and doing so is the obvious next step.

Not answered here: which function does it. This is the behaviour, measured. The image-side half of question 1 — the function that advances the clock and evaluates the segment — is not found yet.