Repository navigation
fix(player): keep the video playing when you jump to another moment - #7719
Open
robinebers wants to merge 2 commits into
Open
robinebers wants to merge 2 commits into
robinebers wants to merge 2 commits into
Conversation
Clicking or dragging the scrubber while a film played left it paused at
the new spot, and `paused` turned true without a `pause` event. The
controls now hold a playing film during the press and play on from where
it is released, like a <video>'s own controls; a paused film stays
paused. A touch scrub no longer replays as mouse events.
seek() gains an opt-in { keepPlaying: true } for hosts (keyboard seeks,
chapter jumps). A plain seek(t), and setting currentTime, still land
paused, so existing callers keep their behavior.
The packed player tarball grows 339 bytes (158705 to 159044, packed with
pnpm 10.17.1 as CI does). main sat 11 bytes under the 1% gate, so the
packed budget moves to 159100; the unpacked size stays within budget.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
miguel-heygen
approved these changes
Nov 14, 2026
miguel-heygen
enabled auto-merge
November 14, 2026 05:35
3 of 4 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
When you watch a video in the HyperFrames player and click somewhere on the progress bar to jump ahead, the video stops. You land on the new moment, frozen, and have to press play again. Dragging along the bar does the same. Every other video player, from YouTube to the browser's own, keeps playing from the new spot, so this feels broken. Apps that draw their own play button also fall out of sync, because the player stops without announcing it:
pausedturns true and nopauseevent fires.What changes
player.seek(30, { keepPlaying: true })jumps without stopping. A plainplayer.seek(30)andplayer.currentTime = 30still stop at the new time, exactly as before, so existing pages keep working the same way.Before
Clicking the bar at 65% while the video plays: it stops at 6.5 s. A drag afterwards leaves it stopped too.
Watch the Before recording (MP4)
After
The same clicks: the video keeps playing from 6.5 s, and again from where the drag ends.
Watch the After recording (MP4)
Test plan
Checked in Chrome 157 with the built player (
dist/hyperframes-player.global.js) against a build ofmain, on four kinds of embed: an opaque-origin composition (sandbox-origin="opaque"), a same-origin composition, a composition that only registerswindow.__timelines, and an MP4 in video mode.pausethenplay, the same order Chrome's own<video controls>uses (pause, seeking, seeked, play, playing).pause/playpair.seek(2, { keepPlaying: true })while playing keeps playing and fires no events;seek(6)still lands paused.bun run --cwd packages/player test(589 tests, 10 new, 6 of which fail onmain), typecheck, oxlint, oxfmt, fallow, the comment checks andbun run verify:packed-manifestspass.How
controls.ts: pressing the scrubber while playing pauses through the existingonPausecallback and remembers that it did; releasing callsonPlay. The play button ignores state changes during that press. When a touch scrub ends, itstouchendis cancelled so the browser doesn't follow the tap with mouse events, which would scrub a second time.hyperframes-player.ts:seek(t, { keepPlaying: true })runs the normal seek, then resumes through the internal play path (no secondplayevent) if the player was playing or had a play queued while its assets loaded.mainpacks to 158,705 bytes, 11 under the old limit. This change adds 235 bytes of code and 104 bytes of README (159,044). The unpacked size stays within its budget.Why
seek()keeps pausing by defaultLanding paused is
seek()'s deliberate contract. Maintainer comments say so (#750, #4054), a test asserts it, and the loop wrap, play-range jumps and the scrubber's own settle rely on it. The runtime (#863) and Studio (#842) both made playing on an opt-inkeepPlayingflag, so the player's API takes the same shape. The built-in controls are different: nothing chose for the scrubber to pause, it inheritedseek()'s contract (#890 noticed that a scrubber click pauses and made the audio match). CONTRIBUTING asks for default changes to be discussed, so treat this part as a proposal. If you'd rather keep today's scrubber behavior by default, it can move behind an attribute.Notes
pausedcan readtruefor a few milliseconds after a keep-playing seek, until the runtime's next state message arrives. The same already happens after a loop wrap, and nothing changes on screen.play()at the end starts over (the player's existing rule, and the HTML one). Compositions end normally.currentTimekeep playing, as it does on a<video>? This PR leaves it landing paused.Related work
keepPlayingto Studio and the runtime.controls.test.ts, so whichever lands second rebases and re-measures.