Commit Graph
11 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 0701fe3856 Share the parts the sound devices and timelines repeated (#85)
feuerwerk / gate (push) Successful in 5m5s
feuerwerk / suite (push) Failing after 18m39s
Round 12 of the clean-up. Duplicate code had grown from 33 clones
(249 lines) after round 11 to 140 clones (1263 lines), almost all of
it in the panels and faces of the sound FX devices (#379 to #401).
It is down to 57 clones (416 lines) now, and src is 816 lines
shorter.

- deviceParts.ts holds what the device panels built by hand: the
  head, rows of knobs, the screen style and the row layout for
  groups of knobs. Twelve panels use it. panel.knobs stays because
  the debug hooks read it.
- DeviceFace is the base of the twelve faces. It finds the node and
  does wired, preview, commit, set, hide, reload and audition.
  compressorWired lives there as paramWired.
- TextPool replaces ten copies of "make texts as needed, hide the
  rest" in the wave and video tabs, the rulers, the piano roll and
  the song and composition timelines. clipStrip.ts is the clip label
  strip the wave and video tabs share.
- core/util/bytes.ts has the concat that ogg, matroska and avcColour
  each wrote out.
- Dead code is gone: clear on the video players (never called since
  #402), posterFileOf and videoTimeAt (only tests called them) and
  easeOut (the same as glide(t, 3)). Values only used in their own
  file lose their export.
- Comments over 125 columns are wrapped, and comments that told the
  history ("as before", "used to") say what is, in src and in the
  e2e modules.
- Left alone on purpose: AnimationEditor and CompositionEditor share
  only the play button, and the curve panels are built differently.
- Chip now places its boxes through put, which rounds. They were
  whole numbers already. The three row layouts are byte-identical
  before and after.
- README lists the video and composition assets and the Videos and
  Compositions pages.

Tests:
- Gate (melektaus-atx): CHECK EXIT 0, 313 files, 4835 tests.
- e2e 25, 34, 64, 65, 66, 69 (melektaus-atx): ALL PASSED, 330 checks.
- TextPool without keeping its texts and framesToRoom without making
  room both go red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:40:05 +02:00
adminandClaude Opus 5.5 39c509a8e5 Keep a video item in a recording when the tab goes back and forth
A recording made while switching between tabs had stretches without
the video item and without its sound, and in between the video stood
for several frames. Two things went wrong behind another tab.

- A player opened there got no first picture. It waited for
  requestVideoFrameCallback, which Chrome never calls behind a tab.
  A recording not in real time then waited 3 s per frame and took it
  without the video. Now the player takes its first picture as soon
  as it has data while the tab is hidden.
- The span of a picture was learnt from steps that skipped pictures.
  After a time behind the tab it learnt 0.133 s instead of 1/30 s,
  left out seeks within that span and never corrected it. Now it
  learns only from steps where the time sought moved at most half a
  step, so no picture can lie between them.
- Behind the tab a seek forgets which picture shows but keeps the
  span. A landing without a frame counts as the same picture only
  once the tab has been in front for 500 ms.

Found in the user's recording 7ea9146b: 5363 frames, all 1/60 s
apart, with the video missing at 10.63 s (8 frames), 43.17 s
(5 frames) and 46.93 s (28 frames), and the sound gone with it.

Tests:
- Unit tests/viewport: 377 green, 4 new or rebuilt. With the old
  rule and the first picture only by requestVideoFrameCallback both
  new tests go red.
- e2e 68 on melektaus-atx: ALL PASSED, 21 checks. New 68l: a video
  item that begins behind another tab has its colours exactly, and
  a tab switched every 350 ms gives colour changes at 0/30/60/90.
- One run in five with switches had a change one frame late, so the
  check allows one frame either way.
- Gate on melektaus-atx: green, 311 files, 4824 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:36:00 +02:00
adminandClaude Opus 5.5 875c76026a Silence a free video once its sprite is destroyed (#420)
A sprite whose Video clock is free played its video on after it was
destroyed, heard in a game, until the player was let go 2 to 4 s
later. A driven one was already held and quiet after 100 ms.

- The library holds and quiets a free player nothing asked for in
  HUSH_MS, as it did a driven one. That covers Destroy, a scene load,
  the end of a run and a sprite switched off.
- Asked for again before it is let go, it plays on from where it was
  held, heard only in a run. A hidden page leaves it alone, as the
  sweep does.
- A sprite only out of view keeps its free video playing and heard.
  Whether a video is heard should not hang on the camera. Before, it
  ran 2 to 4 s and was then let go. Now it plays on, which opens its
  player even where it was never in view.

Tests:
- Unit: video-library 1 new. A free player losing its wearer is held
  and quiet 200 ms later, stays so through a new run, plays on heard
  when asked again with the same element, and runs on on a hidden
  page.
- e2e 66 on melektaus-atx: ALL PASSED, 36 checks, 66g-ii new. Heard
  31 frames before Destroy, held and muted in every frame read after.
- Counter-probe without hushing free players: the unit test and only
  66g-ii fail, the player plays on heard (paused false, muted false).
- Gate on melektaus-atx: 309 files, 4797 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:49:12 +02:00
adminandClaude Opus 5.5 65d7bf6a1f Silence a composition video as soon as its item ends
game-maker / gate (push) Successful in 4m19s
game-maker / suite (push) Failing after 19m52s
A video in a composition plays on a driven player that the item tells
where to stand every frame. When the item ended, its object was gone
and stopped asking, but the player ran on with its sound until it was
let go for being unused. That took 2 to 4 seconds, and in a pause it
never happened at all. The same held for the composition preview.

- A driven player nobody asked for in 100 ms is now paused and muted.
  When its wearer comes back it carries on where it should be, as
  before. It is still let go after 2 to 4 seconds.
- A player still without its first picture keeps playing, without
  sound, because it only gets that picture by playing.
- Leaving the animation or composition tab now silences their video
  preview at once instead of after 2 to 4 seconds.
- Free videos (Video clock free) keep the old behaviour. A sprite
  destroyed by a behavior is still heard for 2 to 4 seconds.

Tests:
- Unit video-library: 10 green (1 new). Without the new timer the
  new test is red.
- e2e 68: ALL PASSED, 11 checks (68g new). An item of 0.5 s with a
  2 s video plays audibly while it is there, and the player is
  paused and muted in every frame from about 200 ms after it ends.
- Gate on melektaus-atx: green, 305 files, 4737 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:24:04 +02:00
adminandClaude Opus 5.5 93190e185c Route a video's sound through its own Master routing node
An editor recording had no sound while the video tab played a video.
The tab played its <video> straight to the speakers, past the Web
Audio graph, so the recording tap behind the limiter never heard it.
Fixing only that would have left videos as the one sound source the
Master routing cannot reach, so they get their own node, like sounds.

- A video asset with sound has a routing node labelled "Video". One
  without sound has none, it would have nothing to send.
- Its links take the sound after volume and FX. Several links or
  another level give the video its own bus, as with a sound.
- A new video follows the bus most other videos use. With none linked
  yet it goes to the Master, not to where the sounds go. Songs and
  sounds no longer follow videos either.
- Players only know their file, so videoBusesOf maps each file to the
  bus of its first asset. setMix takes that map and moves the gains of
  running videos at once when the routing changes.
- In the game, played in the editor or released, a video's sound
  follows its links. The tabs (video, composition, animation preview)
  play straight into the Master, as the sound tabs have since #368.
- The video tab's sound now goes through the Master, so its volume, FX
  and limiter apply there and the editor recording hears it.
- The recording badge beside the menu is an HTML element over the
  canvas now. The second render that drew it on the desynchronized
  canvas let the screen show a frame without it, so it flickered. The
  element is never in the copy, and clicks go through it.

Tests:
- Unit master-routing and sound-library: 3 new, green. Red without
  video in the routing kinds, without moving the gains, and with the
  old rule for the links of a new video.
- e2e 66e-iii: the tab plays a video linked to a bus, the level is
  0.09 on the Master and 0 on the bus. 66g: in the game the bus has
  0.09. Both red with the old ways.
- e2e 67e reads the badge from a real screenshot, still green.
- The README names 2043 e2e checks.
- Gate on melektaus-atx: 304 files, 4694 tests green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:15:59 +02:00
adminandClaude Opus 5.5 36ffd9a61a Land a driven video's seek without a frame so it is never empty (#404)
game-maker / gate (push) Successful in 4m9s
game-maker / suite (push) Successful in 17m18s
Since b9fcf48 a seek of a driven video only counted as done once
Chrome handed over the next frame. For some seeks Chrome hands over
none. The player then waited for good, the view left the sprite out
after 0.3 s, and the picture stayed empty until the next jump.

- A seek now lands after `seeked` with the next frame, or after
  150 ms at the latest. The worst case is a picture 150 ms late.
- The landing is set up in the `seeked` handler instead of `seek`.
  A frame from before the last seek of a chase no longer counts.
- `__gm.videos()` reports `landing`, so an empty picture can be told
  apart from a player that is still loading.

Findings:
- The empty picture could not be reproduced in Chrome headless.
  The test video is 320x180 and decoded in software. The user's
  videos are 1080p60 H.264, likely decoded in hardware.
- The user confirmed the fix in the editor.

Tests:
- Unit video-library + video-follow: 20 green (1 new). The new test
  plays a video that hands over no frame after a seek. With the old
  code it stays not ready, so it fails.
- e2e 66 on melektaus-atx: ALL PASSED, 29 checks. The new 66r cuts
  LATE ten times quickly and was also green with the old code.
- Gate on melektaus-atx: CHECK EXIT 0, 297 files, 4563 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:04:40 +02:00
adminandClaude Opus 5.5 3a1e420806 Read a driven video's speed on the frame clock so 1x stays 1x (#404)
game-maker / gate (push) Successful in 4m12s
game-maker / suite (push) Successful in 16m58s
A driven video measures how fast its wearer's time runs and sets its
playbackRate from that. The time moves on with the ticker, but the
reading took its clock from performance.now() while drawing. A frame
that had more to do before the draw shifted that clock against the
time, and a 1x animation read as up to 1.09. Outside 0.9 to 1.1 the
video goes mute, so the sound could drop out for a moment. In CI this
made e2e 66k red now and then.

- VideoLibrary reads the speed with the timestamp of the frame
  (document.timeline.currentTime), the same clock the time runs on.
  It falls back to performance.now() where there is no document.
- debugInfo() reports the measured speed of a driven player.
- e2e 66k checks the measured speed and the correction of the rate
  apart. The rate alone may differ from 1 by up to 10 percent on
  purpose, so checking it at +-0.1 was too strict.

Tests:
- Unit video-library: 8 tests green, 1 new. With performance.now()
  back in place the new test is red and reads 1.089.
- e2e 66 on melektaus-atx: all passed, 27 checks, speed 1.000 in
  all 5 readings.
- Gate on melektaus-atx: CHECK EXIT 0, 297 files, 4562 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:49:45 +02:00
adminandClaude Opus 5.5 dd60a795e1 Wait for driven videos before a run starts so no sprite flashes (#404)
game-maker / gate (push) Successful in 4m28s
game-maker / suite (push) Failing after 16m24s
The game view opens a player for each driven video only when it draws,
and it draws only while its tab is visible. Without a detour over the
Game tab the players came up with the click on Play. Until a video had
loaded, its sprite showed its base colour, white in the user's cut.
Sharing the players of the scene view would not have helped. From the
Animations tab those are discarded after two to four seconds.

- Play holds the run at second 0 until every driven video in the game
  view has its first picture, at most 3 s. The status line says
  "Loading the videos… · Esc stops".
- The game clock stands while it waits. Behaviors and sounds start
  only after it, so picture and sound stay together.
- Load scene in a running game waits the same way.
- A driven video without a picture is not drawn at all instead of
  white. That holds everywhere, also on a switch to the Game tab
  without Play and at a cut whose video is not ready.
- The exported player waits the same way.
- __gm.playInfo() reports waiting for the e2e check.

Tests:
- e2e 66p puts the sprite in front of the camera and waits 4.5 s so
  the game view has no player left. Then it reads every frame of the
  run: 14 frames of waiting on #22252c at time 0, then blue, no white.
- Against the old code 66p fails: 14 frames #ffffff, then blue.
- Unit video-library: 7 passed, 1 new for loading().
- e2e 66 on melektaus-atx: ALL PASSED, 27 checks.
- Gate on melektaus-atx: exit 0, 297 files, 4558 tests.
- tsc, eslint, Prettier clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:07:53 +02:00
adminandClaude Opus 5.5 43fd22a763 Keep a driven video ready while its sprite is off (#404)
A cut between two videos flashed white: the sprite switched on had
no picture yet, because its player only opened then or had been
dropped meanwhile. A driven video now waits muted at its time while
its sprite is off, in the editor and in a run.

- A disabled sprite with Video clock Driven asks for its player
  without drawing it. A Free video still opens when shown.
- A run's draw list keeps disabled driven sprites, marked as off.
  Before, collectWorldItems dropped every disabled object, so the
  game view never held a player for them.
- VideoLibrary.wear() counts as asking. The sweeper dropped a held
  player after 2 to 4 s and it was loading again at the cut.
- The speed of the asked time is read over a window that grows to
  500 ms. Over 100 ms one 1/60 s step more or less read as 17 % off,
  the playback rate jumped and the sound dropped out.
- A stream feeds every packet it has loaded to the decoder at once
  and waits once. One round trip per 20 ms packet let the decoder
  fall behind on a busy main thread, which made e2e 34 flaky in CI.

Tests:
- Unit streaming, video-library, video-follow: green, 4 new
- e2e 34 + 66 (atx): ALL PASSED, stream skipped 0, 66m new
- e2e 66 (atx), two runs: ALL PASSED, 25 checks, 66n new
- Gate (atx): CHECK EXIT 0, 297 files, 4557 tests
- Counter-checks: without the draw list fix 66n finds no player,
  without the wear() fix and with the old speed reading the new
  unit tests turn red

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:37:16 +02:00
adminandClaude Opus 5.5 c9e8a3f6ac Let an animation drive a sprite's video picture by picture (#404)
game-maker / gate (push) Successful in 4m16s
game-maker / suite (push) Failing after 16m12s
A video used to run on its own clock as soon as a sprite wore it. An
animation could switch the sprite on and off, but never say which
picture shows at which frame. Cutting a video in an animation needs
the opposite: the animation sets the time and the video follows.

- Sprites wearing a video or a clip get two rows, Video clock and
  Video time. They show only while the texture holds a video.
- With Video clock on Driven the video does not play by itself. It
  shows the picture at Video time, counted from the clip's start.
  Past the end it goes round with Loop and holds the last picture
  without.
- Video time is a number like any other, so keys, step, easing and
  curves give stills, slow motion and ramps.
- A driven sprite gets a player of its own, keyed by the wearer's
  name. The same clip can run twice at different times.
- Following has two ways (`videoFollow.ts`). Running forward at 0.25x
  to 4x the video plays at that rate and is nudged. A still, a scrub
  or a step backwards seeks, and only the newest seek counts.
- Sound is heard only near 1x. The animation tab now plays it too, so
  a cut can follow the sound. Scrubbing is silent.
- Scene, game view, the inspector's PREVIEW and the shader preview
  show the driven picture. In a running game Play animation sets it.

Found on the way:

- After a scrub the video in the animation tab kept running. The
  preview redrew only while the playhead moved, so nobody asked for
  the video once it stood. `AnimationEditor.tick` now redraws it every
  frame while it shows a video.
- Every sprite has two more defaults (`videoClock: 'free'`,
  `videoTime: 0`). Eight tests in five files pinned the old list and
  are updated.

Tests:
- Unit: video-clock and video-follow new, 4 files with 42 tests green.
- e2e 66 (melektaus-atx): ALL PASSED, 22 checks, 3 new. 66i: two
  sprites on one clip stand at 0.2 s red and 0.7 s green. 66j: a
  scrub to frames 2, 7, 12 and 3 shows red, green, blue, red, and
  each colour still stands 0.7 s later. 66k: playing runs 10-30 ms
  behind at rate 1.00-1.03 and is not muted.
- Counterchecks: a seek that keeps the rate turns the seek test red.
  Not seeing Driven turns 66i, 66j and 66k red.
- Gate (melektaus-atx): exit 0, 297 files, 4546 tests.
- Not tested in the browser: Play animation in a running game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:23:07 +02:00
adminandClaude Opus 5.5 c815d2c35f Keep a playing video when the window comes back to the front (#402)
Chrome draws no frames for a hidden page, so the scene stopped asking
for its videos. The sweeper of the VideoLibrary kept running and
dropped every player after 2 s. Back in the window, the video started
over, and Pixi warned that a texture source was destroyed while still
bound to a shader.

- The sweeper skips a hidden page. The time hidden does not count as
  idle, the 2 s start once the page is shown again.
- A dropped player only unloads its video source instead of destroying
  it. A shader that still holds the source no longer warns.
- CI serves videos through a new router for php -S. The built-in
  server answers no Range requests, so Chrome could not seek. e2e 66
  had 4 red checks in the suite job of d48f174 because of it. nginx on
  stein2 hid this.

Tests:
- Unit video-library.test.ts (new): 3 tests
- e2e 66 (atx): ALL PASSED, 18 checks
- Router checked with curl: 206 with the right bytes
- Counter-check: the old sweep fails both hidden-page tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:32:59 +02:00