Record a game from its very first frame
game-maker / gate (push) Successful in 4m18s
game-maker / suite (push) Successful in 18m17s

Start recording has to set up its encoders first. It asks the browser
whether it can encode H.264 at the size and hooks in the sound tap,
both asynchronously, and it only took its size at the first frame.
Meanwhile the game ran on unrecorded. A caption that fades in from
alpha 0 at the start was already visible in the first recorded frame.

- A run now waits while a recording sets up its encoders, at most 3 s,
  as it already waits for its videos at Play. That holds in the editor
  and in play.html.
- The recording takes its size at Play, right after the game view has
  the world, rather than at the first frame. A Start recording in
  On start is so ready before the first tick.
- A recording started in the middle of a game holds the picture for
  that moment. A sound that already plays goes on.

Tests:
- tsc, eslint, Prettier clean
- e2e 67 on melektaus-atx: ALL PASSED, 11 checks (67a-ii new). The
  first recorded frame is at game time 0.0168 s, the first frame.
- Without the wait 67a-ii is red: the first recorded frame is at
  0.1667 s, so 10 frames were lost.
- Gate on melektaus-atx: green, 305 files, 4740 tests, run on this
  commit and the one before together.
- Not checked in the browser: a start in the middle of a game, and
  play.html.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-13 10:11:27 +02:00
1 parent 2ba41c1bd0
commit 3443368855
7 files changed
+68 -6

No files matched your search

+4 -1
View File
@@ -1540,7 +1540,10 @@ export class Editor implements PlayHost {
playing: boolean;
paused: boolean;
gamePaused: boolean;
/** Held at the start until the Game tab's driven videos have a picture (#404). */
/**
* Held at the start until the Game tab's driven videos have a picture (#404), or while a
* recording sets up its encoders.
*/
waiting: boolean;
time: number;
sceneId: string;
+7 -3
View File
@@ -127,9 +127,12 @@ export class PlaySession {
return this.world?.paused ?? false;
}
/** Held at its start until the Game tab has a picture of its driven videos (#404). */
/**
* Held at its start until the Game tab has a picture of its driven videos (#404), and while a
* recording sets up its encoders.
*/
get isWaiting(): boolean {
return this.videoWait !== null;
return this.videoWait !== null || this.recorder.isStarting;
}
/** Pause or resume; before Play it switches whether Play starts paused. */
@@ -235,6 +238,7 @@ export class PlaySession {
this.host.gameView.setWorld(this.run.world);
this.host.showGame();
this.waitForVideos();
this.recorder.prepare();
if (paused) {
this.state.setPaused(true);
this.applyPause(false);
@@ -331,7 +335,7 @@ export class PlaySession {
const run = this.run;
if (!run) return;
const world = run.world;
if ((this.state.paused && !this.stepping) || this.waitsForVideos()) {
if ((this.state.paused && !this.stepping) || this.waitsForVideos() || this.recorder.isStarting) {
// Keys and clicks that happen while paused are dropped instead of piling up – otherwise
// resuming would deliver every one of them in a single frame.
this.input.endFrame();