mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
* fix(player): parent-frame media playback for mobile Mobile browsers block media.play() inside iframes when the user gesture happened in the parent frame — postMessage doesn't transfer user activation (per the User Activation v2 spec). ## Problem The player renders compositions in a sandboxed iframe. When a user taps play in the parent frame, the player sends a postMessage to the iframe's runtime, which calls audio.play(). On mobile, this fails silently because the iframe has no user activation context. ## Solution The player now extracts ALL timed media elements (audio/video with data-start) from the iframe's DOM (same-origin access), creates parent-frame copies, and disables the iframe originals. On play(), parentMedia.play() runs synchronously in the gesture call stack, satisfying mobile autoplay policy. ### Generic media handling - Finds all `audio[data-start], video[data-start]` in the iframe - Creates a parent-frame copy for each (Audio or Video element) - Preserves data-start offsets for correct seek positioning - Strips data-start from iframe elements so the runtime ignores them - Falls back to iframe media for cross-origin iframes ### `audio-src` attribute Convenience for the common single-narration case. When set, the player starts preloading audio immediately — before the iframe loads. This eliminates the loading delay that caused jittery playback. ### No active sync Both parent media and the GSAP timeline are real-time systems. When started simultaneously, they naturally stay within ~10ms — no drift correction needed. Active sync with coarse granularity (50ms polling) caused MORE jitter than it prevented via repeated audio seeks. ## CI - Added unified `test` job replacing separate per-package test jobs - Added root `test` script: `bun run --filter '*' test` - New packages with test scripts are automatically included - Added happy-dom for player DOM tests ## Tests - 10 new tests for parent-frame media: preloading, play, pause, seek, muted/rate sync, cleanup, attribute changes - All 21 player tests pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shader-transitions): pass CI when no test files exist Add --passWithNoTests to vitest run so the unified test job doesn't fail on packages that have a test script but no test files yet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): update tests for new id field and GSAP lint rule - normalize.test.ts: loadTranscript now assigns id fields (w0, w1, etc.) to SRT/VTT results and empty string for words-json passthrough - lintProject.test.ts: add GSAP CDN script to validHtml() fixture to satisfy the missing_gsap_script lint rule added in core Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): add missing data-start/data-duration to validHtml fixture The validHtml() test fixture was missing data-start and data-duration attributes, triggering the root_composition_missing_data_start and root_composition_missing_data_duration lint warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): fetch LFS objects for producer test job Producer regression tests compare rendered output against reference MP4 files stored in git LFS. Without lfs: true, checkout fetches pointer files instead of actual videos, causing "moov atom not found" errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: remove redundant test-producer job The regression workflow already runs the same 28 producer fixtures in a Docker container with prod-matching Chrome/fonts/ffmpeg, sharded across 8 parallel matrix jobs with 40-min timeouts. The CI test-producer job was a duplicate that ran on bare runners with worse determinism and a 15-min timeout too short for all fixtures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hyperframes/shader-transitions
WebGL shader transitions for HyperFrames compositions. Renders GPU-accelerated scene-to-scene transitions using fragment shaders, driven by GSAP timelines.
Install
npm install @hyperframes/shader-transitions
Or load directly via CDN:
<script src="https://cdn.jsdelivr.net/npm/@hyperframes/shader-transitions/dist/index.global.js"></script>
Usage
import { init } from "@hyperframes/shader-transitions";
const tl = init({
bgColor: "#0a0a0a",
accentColor: "#ff6b2b",
scenes: ["scene-1", "scene-2", "scene-3"],
transitions: [
{ time: 3, shader: "domain-warp", duration: 0.8 },
{ time: 8, shader: "light-leak", duration: 0.7 },
],
});
The init() function captures each scene to a WebGL texture at transition time, crossfades between them using the selected shader, and returns a GSAP timeline. If WebGL is unavailable, it falls back to hard cuts.
With an existing timeline
Pass your own GSAP timeline to layer transitions onto it:
const tl = gsap.timeline({ paused: true });
// ... add your scene animations ...
init({
bgColor: "#000",
scenes: ["intro", "demo", "outro"],
transitions: [
{ time: 5, shader: "cinematic-zoom" },
{ time: 12, shader: "glitch", duration: 0.5 },
],
timeline: tl,
});
Available shaders
| Shader | Description |
|---|---|
domain-warp |
Organic noise-based warp with glowing edge |
ridged-burn |
Ridged noise burn with sparks and heat glow |
whip-pan |
Horizontal motion blur simulating a fast camera pan |
sdf-iris |
Circular iris wipe with glowing ring edge |
ripple-waves |
Concentric ripple distortion radiating from center |
gravitational-lens |
Warping gravity well with chromatic aberration |
cinematic-zoom |
Radial zoom blur with chromatic fringing |
chromatic-split |
RGB channel separation expanding from center |
glitch |
Digital glitch with block displacement and scanlines |
swirl-vortex |
Spiral rotation with noise-based warping |
thermal-distortion |
Heat shimmer rising from the bottom |
flash-through-white |
Flash to white then reveal the next scene |
cross-warp-morph |
Noise-driven morph blending both scenes |
light-leak |
Warm cinematic light leak with lens flare |
API
init(config): GsapTimeline
| Option | Type | Required | Description |
|---|---|---|---|
bgColor |
string |
yes | Background color (hex) for scene capture |
accentColor |
string |
no | Accent color (hex) for shader glow effects |
scenes |
string[] |
yes | Element IDs of each scene, in order |
transitions |
TransitionConfig[] |
yes | Transition definitions (see below) |
timeline |
GsapTimeline |
no | Existing timeline to attach transitions to |
compositionId |
string |
no | Override the data-composition-id for timeline registration |
TransitionConfig
| Option | Type | Default | Description |
|---|---|---|---|
time |
number |
— | Start time in seconds |
shader |
ShaderName |
— | Shader name from the table above |
duration |
number |
0.7 |
Transition duration in seconds |
ease |
string |
"power2.inOut" |
GSAP easing function |
SHADER_NAMES
Array of all available shader name strings, useful for validation or building UIs.
import { SHADER_NAMES } from "@hyperframes/shader-transitions";
// ["domain-warp", "ridged-burn", "whip-pan", ...]
Distribution
| Format | File | Use case |
|---|---|---|
| ESM | dist/index.js |
Bundlers (Vite, webpack, etc.) |
| CJS | dist/index.cjs |
Node.js / require() |
| IIFE | dist/index.global.js |
<script> tag, CDN |
All formats include source maps. TypeScript definitions included.
Related packages
@hyperframes/core-- types, parsers, runtime@hyperframes/engine-- rendering enginehyperframes-- CLI
License
MIT