# What happens between two screens — a fade through black, and where its timing lives **Status:** ✅ `CONFIRMED`. The quad and its ramp are **decoded** (a keyframe group on the disc, with a disc-wide check); the wall-clock timings are **measured** off the running game at 30 fps. One piece is **undecodable from this field** and is called out below. Answers [MISSION Q7](../port/MISSION.md). ## The mechanism — decoded Every title-side screen carries a full-screen untextured primitive that is **black, and paints last**: `pteff00.prm` in `GP_TITLE`, `pfeff00.prm` in `GP_SAVE_LOAD`. That it sorts last was already established ([`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md)); what is new here is that its **keyframe group is the transition**. The group is always four blocks, and always this shape: | block | alpha | meaning | |---|---|---| | 1 | `0xff` at `t = T0` | the screen starts **black** | | 2 | `0x00` at `t = T1` | ramp to fully clear — the screen **fades in** | | 3 | `0x00` at `t = T2` | clear; this is the resting pose, the quad is invisible | | 4 | `0xff`, **no time** | ramp back to black — the screen **fades out** on exit | Read with the corpus's rule that a keyframe is the *start* of a ramp ([`structures/ui-resting-pose.md`](structures/ui-resting-pose.md)). ``` $ tools/re-capture/fade_quads.py 4 5 6 # GP_TITLE build 4 (title) pteff00.prm t=16 α=255 t=261 α=0 t=269 α=0 t=— α=255 build 5 (main menu) pteff00.prm t=12 α=255 t= 70 α=0 t= 80 α=0 t=— α=255 build 6 (EXTRAS) pteff00.prm t=12 α=255 t= 64 α=0 t= 74 α=0 t=— α=255 ``` Under [Q1](ui-keyframe-time-unit.md)'s `1 unit = 1/60 s`: the screen holds black for **0.20 s**, then fades in over **0.87 s** (`EXTRAS`), **0.97 s** (main menu) or **4.08 s** (the title). ### ❔ The fade-OUT duration is not in this field The fourth block has **no time** — a group's last block stops 4 bytes short and that word is already the next group's element index ([`ui_layout.rs`](../../crates/sylpheed-formats/src/ui_layout.rs) documents the packing). So the disc gives the ramp's *target* (black) and not its length. That duration is **measured** below, and the port is authoring it. ### The disc-wide check, and what it shows about overlays Over every `GP_*.pak`, counting bundles that have a `.prm` element **and** ≥8 elements (screen-sized rather than a two-element fragment): > **40 of 97** carry a 255 → 0 → 255 quad; 56 of the 60 found disc-wide have > exactly 4 keyframes. 41 % sounds weak until it is read per pak, where it is nearly all-or-nothing: | pak | with quad / screen-sized | |---|---| | `GP_MISSION_SELECT`, `GP_MOVIE_THEATER`, `GP_OPTIONS`, `GP_TUTORIAL`, `GP_SYSTEM` | 2/2 each | | `GP_BUNK`, `GP_CHALLENGE` | 6/6 | | `GP_STAGE_CLEAR` | 4/4 | | `GP_SAVE_LOAD` | 10/12 | | **`GP_TITLE`** | **6/12** | | `GP_DIALOG` | 0/133 | | `GP_DEBRIEFING_PILOTLOG` | 4/102 | **`GP_TITLE`'s 6 of 12 is the interesting row, and it is not a gap.** The six that carry the quad are exactly the six *screen* builds — title, main menu and `EXTRAS`, English and Japanese. The six that do not are exactly the six **overlays**: the `PRESS Ⓐ BUTTON` plate and the two `DELTASABER` plates ([`ui-title-build-map.md`](ui-title-build-map.md)). An overlay composited onto a screen has no transition of its own, so it has no fade quad — which is independent corroboration that those builds are overlays rather than screens. `GP_DIALOG`'s 0/133 says the same thing about dialog boxes. ## The timing — measured Recorded with `ffmpeg -f x11grab -framerate 30` over the game surface, mean frame luminance per frame; raw data in [`captures/transitions/transition-luminance.csv`](captures/transitions/transition-luminance.csv), filmstrip in [`transition-filmstrip.png`](captures/transitions/transition-filmstrip.png). | | main menu → `EXTRAS` (Ⓐ) | `EXTRAS` → main menu (Ⓑ) | |---|---|---| | press → first visible change | 0.07 s | 0.37 s | | **fade-out to black** | **0.367 s** | **0.400 s** | | **pure black** (luminance 0.02) | 0.233 s | 0.167 s | | luminance rise until settled | 0.567 s | 1.467 s | The fade-out is the number the file cannot give, and it comes out the same both ways: **~0.4 s**, i.e. ~24 units under Q1's rule. The black hold is *consistent with* the file's 12 units (0.20 s) but does not confirm it — the plateau spans the tail of the outgoing screen's fade-out and the head of the incoming screen's black, and this measurement cannot separate them. ### ⚠️ The luminance rise is **not** the quad's ramp The two columns differ by 2.6× where the quad's declared ramps differ by only 1.12× (58 units vs 52). The filmstrip says why: the background reappears *first* and the labels arrive after it, so what the luminance curve is timing is the incoming screen's **own element animations**, not the fade quad. Quoting 1.47 s as "the main menu's fade" would be wrong. The quad's ramp is decoded; the screen's build-in is a separate, longer thing. ## For the port * a screen change is: **fade the outgoing screen to black over ~0.4 s**, hold black briefly, then **fade the incoming screen in over its own declared ramp** while its elements play their own keyframes; * the fade-in ramp is **read from the file** (`T1 − T0` on the screen's fade quad); * the ~0.4 s fade-out and the black hold are **authored from this page** — the disc does not carry them.