Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit a16b148978927c0174e2a86d05de324cbfee1baa
parent bf7e74954d5c4384c61a6495cf28dcf581e96141
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu,  1 Oct 2026 14:50:09 -0400

umtool: the following picture is pulled back to the sound, and its own pause no longer moves the playhead

During a Web Audio playback the muted picture is re-seeked when it drifts
from the audio clock by more than 0.25 s (checked twice a second): after a
seek into a partly loaded file it starts late by however long the bytes took.

The element's timeupdate moves the playhead only while the element plays by
itself; a scrub on its paused bar moves it through `seeked`. The timeupdate
its own pause fires at the end of a Web Audio playback used to put the
playhead back where the picture had got to.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mumtool/components/projects/ClipBench.tsx | 23++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/umtool/components/projects/ClipBench.tsx b/umtool/components/projects/ClipBench.tsx @@ -934,13 +934,25 @@ export default function ClipBench({ data }: { data: ClipBenchData }) { // The playhead at ~30 Hz, not every frame: each update re-renders the // bench, and the line moving smoothly is not worth that at 60. + // + // And the picture is pulled back to the sound when it drifts by more + // than a quarter second -- which it does after a seek into a file that + // is only partly loaded, where the element starts late by however long + // the bytes took. let painted = -1; + let checked = t0; const tick = () => { if (session.current !== s) return; const now = ctx.currentTime; + const pos = playheadAt(from, t0, now, rate, sch.duration); if (now - painted >= 1 / 30) { painted = now; - setPlayhead(playheadAt(from, t0, now, rate, sch.duration)); + setPlayhead(pos); + } + if (el && now - checked >= 0.5 && now > t0) { + checked = now; + const want = pos - fetchStart; + if (!el.seeking && Math.abs(el.currentTime - want) > 0.25) el.currentTime = want; } s.raf = requestAnimationFrame(tick); }; @@ -1004,7 +1016,14 @@ export default function ClipBench({ data }: { data: ClipBenchData }) { useEffect(() => { const el = video.current; if (!el) return; + // While it plays by itself. A paused element's `timeupdate` is the one its + // own pause fires -- after a Web Audio playback, that is the muted picture + // stopping wherever it had got to, which is not where the sound stopped. const tick = () => { + if (!session.current && !el.paused) setPlayhead(el.currentTime + fetchStart); + }; + // A scrub on the paused element's own bar. + const seeked = () => { if (!session.current) setPlayhead(el.currentTime + fetchStart); }; const paused = () => { @@ -1015,9 +1034,11 @@ export default function ClipBench({ data }: { data: ClipBenchData }) { if (session.current) stopPlayback(); }; el.addEventListener("timeupdate", tick); + el.addEventListener("seeked", seeked); el.addEventListener("pause", paused); return () => { el.removeEventListener("timeupdate", tick); + el.removeEventListener("seeked", seeked); el.removeEventListener("pause", paused); }; }, [fetchStart, stopPlayback, cachedName]);