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:
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]);