Files
hyperframes/packages/core/src/runtime
Vance IngallsandClaude Opus 4.8 1ff99a50f5 fix(core): play bounded WebAudio clips full-length at non-1x playback rate (#1494)
* fix(core): play bounded WebAudio clips full-length at non-1x playback rate

startBoundedSource passed `clipDuration * rate` as start()'s duration arg, but
that arg is buffer-content seconds while clipDuration is composition seconds.
Media advances 1:1 with composition (the global rate scales the transport clock
and the source playbackRate together), so the content to play is exactly
clipDuration. Multiplying by rate truncated the clip at rate < 1 (audio cut out
at the midpoint on half-speed playback) and overran it at rate > 1. Drop the
multiply — playbackRate alone stretches the fixed content to the right wall time.

Adds a half-speed regression test and corrects the prior test that asserted the
rate-scaled (overrunning) bound.

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

* fix(core): restore the mediaRate scaling on the WebAudio clip bound

The bound this branch removed was correct. `start()`'s duration argument is
buffer seconds, and an element with `data-playback-rate="2"` consumes two
buffer seconds per composition second, so a `clipDuration`-second clip needs
`clipDuration * mediaRate` of buffer. Wall time then works out as
`(clipDuration * mediaRate) / (mediaRate * globalRate) = clipDuration /
globalRate`, which is the transport duration that was wanted. Dropping the
factor truncated authored 2x clips at their midpoint and overran authored
0.5x ones — and the sibling line still scaled `sourceElapsed` by mediaRate,
so `remaining` mixed buffer with composition seconds and only landed right at
mediaRate = 1.

The branch's half-speed regression could not have caught this: it changed the
GLOBAL rate on an element whose authored rate is 1, and the global rate
cancels out (it scales the transport clock and the source's playbackRate
together). Both formulas return 10 there, so the test passed before the
change it was meant to justify.

Replaced with the two cases that do discriminate — a clip authored at 2x and
one at 0.5x, each asserting the buffer-second bound. Both fail if the factor
is dropped again, as does the pre-existing authored-2x/global-0.5x contract
test the removal was breaking.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-20 23:37:30 -07:00
..

Hyperframe Runtime Engine

This folder owns the runtime that powers preview and producer parity.

Current Direction

  • Runtime source of truth is converging on hyperframe.ts.
  • Build produces:
    • dist/hyperframe.runtime.iife.js (browser bootstrap)
    • dist/hyperframe.runtime.mjs (tooling/tests)
    • dist/hyperframe.manifest.json (version + sha256 + artifact map)
  • FE owns iframe runtime injection.
  • BE persists raw generated HTML without injecting runtime scripts.
  • Producer validates pinned runtime checksum from manifest before render.

Runtime Contract (Stable Surface)

Globals:

  • window.__player
  • window.__playerReady
  • window.__renderReady
  • window.__timelines
  • window.__clipManifest

postMessage:

  • parent -> runtime control:
    • source: "hf-parent"
    • type: "control"
    • actions: play, pause, seek, set-muted, set-playback-rate, enable-pick-mode, disable-pick-mode
  • runtime -> parent events:
    • source: "hf-preview"
    • type: "state" and type: "timeline"
    • type: "ready" — emitted once when installRuntimeControlBridge registers the control-message listener. The parent uses it to replay current playback state (set-muted, set-volume, set-playback-rate) so any control message sent before the listener was installed isn't lost. Emitted again on every iframe reload because the new runtime instance starts with no state.

Determinism baseline:

  • renderSeek is the producer-canonical seek path.
  • 30fps quantization and readiness gates are correctness requirements.

Build

bun run --filter @hyperframes/core build:hyperframes-runtime

Security Expectations

  • Runtime bootstrap URL must be version-pinned and host-allowlisted.
  • Iframe bridge payloads must be schema-validated.
  • Unsafe URL schemes (javascript: and unapproved data:) are rejected.
  • Fail closed if runtime bootstrap/handshake is not healthy.

Product Editing Model

  • Primary mode: prompt + element picking.
  • Secondary mode: manual precision controls.
  • Avoid timeline-first manual workflows as default product path.