Skip to content

Commit 409e31b

Browse files
committed
docs(player): clarify sub-frame vs frame-quantized seek precision
Per reviewer feedback on #397: the same-origin path forwards `timeInSeconds` verbatim to `__player.seek` (sub-frame precision), while the postMessage fallback rounds to the nearest integer frame for transport. Surface that asymmetry on the public `seek` JSDoc so external callers (notably studio scrub UIs) understand which transport is in effect for their embed. Doc-only — no behavior delta.
1 parent 1cf0456 commit 409e31b

1 file changed

Lines changed: 24 additions & 5 deletions

File tree

packages/player/src/hyperframes-player.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,12 +263,31 @@ class HyperframesPlayer extends HTMLElement {
263263
this.dispatchEvent(new Event("pause"));
264264
}
265265

266+
/**
267+
* Move playback to `timeInSeconds`.
268+
*
269+
* Two transports, with different precision semantics — read this before
270+
* writing assertions against `seek` from outside the player:
271+
*
272+
* - **Same-origin (sync) path** — when the runtime's `window.__player.seek`
273+
* is reachable, we call it directly. `timeInSeconds` is forwarded
274+
* *verbatim* (no rounding), so a same-origin scrub of `seek(7.3333)`
275+
* lands the runtime at `7.3333 s` — sub-frame precision relative to
276+
* `DEFAULT_FPS` (30). Studio scrub UIs that need fractional-frame
277+
* alignment (e.g. waveform scrubbing on long-duration audio) get the
278+
* exact requested time.
279+
* - **Cross-origin (postMessage) path** — when same-origin access throws
280+
* or `__player.seek` is missing, we fall back to the postMessage bridge.
281+
* The wire protocol carries integer frames (`frame: Math.round(t × FPS)`),
282+
* so cross-origin embeds are *frame-quantized* and `seek(7.3333)` lands
283+
* at `Math.round(7.3333 × 30) / 30 ≈ 7.3333…` (same value here, but for
284+
* most fractional inputs you'll see a snap to the nearest 1/30 s).
285+
*
286+
* `this._currentTime` always reflects the *requested* `timeInSeconds`
287+
* regardless of transport, so the controls UI shows the un-quantized value
288+
* either way; the asymmetry only affects what the runtime actually paints.
289+
*/
266290
seek(timeInSeconds: number) {
267-
// Prefer the same-origin sync path — `__player.seek` lands the new frame
268-
// in the same task as the input event, so scrub UIs see immediate visual
269-
// feedback. Cross-origin embeds (anywhere reading `contentWindow.__player`
270-
// throws or returns nothing) fall back to the postMessage bridge, which
271-
// preserves the original async semantics for external hosts.
272291
if (!this._trySyncSeek(timeInSeconds)) {
273292
const frame = Math.round(timeInSeconds * DEFAULT_FPS);
274293
this._sendControl("seek", { frame });

0 commit comments

Comments
 (0)