diff --git a/skills-manifest.json b/skills-manifest.json
index 1e00398a37..9cfb6eee92 100644
--- a/skills-manifest.json
+++ b/skills-manifest.json
@@ -70,8 +70,8 @@
"files": 30
},
"remotion-to-hyperframes": {
- "hash": "13ade041e6416e5b",
- "files": 70
+ "hash": "65222f2d894e8776",
+ "files": 77
},
"slideshow": {
"hash": "71174bff2d869f18",
diff --git a/skills/remotion-to-hyperframes/SKILL.md b/skills/remotion-to-hyperframes/SKILL.md
index 5981de366f..b56a8efc41 100644
--- a/skills/remotion-to-hyperframes/SKILL.md
+++ b/skills/remotion-to-hyperframes/SKILL.md
@@ -74,10 +74,9 @@ Don't load all of them — load only what the specific source needs.
Emit `index.html` with:
- Root `
` carrying the composition's `data-composition-id`, `data-start="0"`, `data-duration` (in seconds), `data-fps`, `data-width`, `data-height`, plus one `data-*` per scalar prop.
-- A flat list of scene divs with `data-start` / `data-duration` / `data-track-index`.
-- Inline `
+
HELLO
+
+
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html
index df2570b8f4..68ee4195b3 100644
--- a/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html
@@ -15,18 +15,6 @@
background: #0a0a0a;
font-family: Helvetica, Arial, sans-serif;
}
- .title {
- position: absolute;
- inset: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #ffffff;
- font-size: 160px;
- font-weight: 800;
- letter-spacing: 0.05em;
- opacity: 0;
- }
@@ -39,22 +27,19 @@
data-duration="3"
data-fps="30"
>
-
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md
index 17659a6835..f2a1716020 100644
--- a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md
@@ -18,16 +18,16 @@ If a translation passes T2, the skill correctly handles `` boundaries,
## Translation walk-through
-| Remotion | HyperFrames |
-| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
-| `` | `` |
-| `spring({frame, fps, config: {damping:12, stiffness:100, mass:1}})` | `gsap.to(target, { scale: 1, duration: 0.7, ease: "back.out(1.4)" })` |
-| `
` | `
` |
-| ` ` | ` ` (with setup.sh copying into both trees) |
-| `interpolate(frame, [0, 15], [0, 1])` at 30 fps | `gsap.to(target, { opacity: 1, duration: 0.5, ease: "none" })` |
+| Remotion | HyperFrames |
+| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
+| `` | host `` |
+| `spring({frame, fps, config: {damping:12, stiffness:100, mass:1}})` | `gsap.to(target, { scale: 1, duration: 0.7, ease: "back.out(1.4)" })` |
+| `
` | `
` |
+| ` ` | ` ` inside `compositions/scene-2.html` (with setup.sh copying into both trees) |
+| `interpolate(frame, [0, 15], [0, 1])` at 30 fps | `gsap.to(target, { opacity: 1, duration: 0.5, ease: "none" })` |
The scene crossfading is a HyperFrames idiom, not a Remotion one: at scene boundaries
-we `gsap.set(scene, { opacity: 0 })` so the previous scene disappears at the
+each scene sub-composition sets its own `gsap.set(scene, { opacity: 0 })` at its local end so the scene disappears at the
right time. Remotion does this implicitly by virtue of ``'s durationInFrames.
## How to render and evaluate
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json
index ed5c477df5..df13d4b6db 100644
--- a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json
@@ -9,19 +9,19 @@
"height": 720,
"ssim_threshold": 0.95,
"validation": {
- "measured_mean_ssim": 0.985,
- "measured_min_ssim": 0.963,
- "measured_p05_ssim": 0.966,
- "measured_p95_ssim": 0.999,
- "measured_at": "2026-04-27",
- "measured_against": "remotion@4.0 (PNG output, BT.709) vs hyperframes@0.4.15-alpha.1",
- "notes": "Spring → back.out(1.4) translation came out cleaner than expected; mean 0.985 leaves substantial headroom over 0.95."
+ "measured_mean_ssim": 0.986,
+ "measured_min_ssim": 0.964,
+ "measured_p05_ssim": 0.967,
+ "measured_p95_ssim": 1,
+ "measured_at": "2026-09-18",
+ "measured_against": "remotion@4.0 (PNG output, BT.709) vs hyperframes@0.7.71 (sub-composition fixtures)",
+ "notes": "Spring → back.out(1.4) translation came out cleaner than expected; mean 0.986 leaves substantial headroom over 0.95."
},
"translation_notes": [
"spring({damping:12, stiffness:100, mass:1}) → back.out(1.4) over 0.7s. Spring overshoot+settle and back.out's overshoot+settle have similar shape; budget ~0.02 SSIM for the late-tail curvature mismatch (validated lower than predicted in spec; original notes overestimated drift).",
- " → wrapping div with data-start/data-duration in seconds and explicit gsap.set(opacity, 0/1) at scene boundaries to crossfade in/out cleanly",
+ " → a sub-composition host with data-start/data-duration in seconds; the scene's own file uses local time and explicit gsap.set(opacity, 0/1) at its boundaries to crossfade in/out cleanly",
" → ",
- " → ; setup.sh copies the asset into both fixture trees",
+ " → (`../assets/x` inside the scene sub-composition file); setup.sh copies the asset into both fixture trees",
"interpolate with default linear easing → ease:'none' in GSAP",
"Fonts again rely on system Helvetica/Arial; ~0.015 SSIM cost from AA differences"
],
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html
new file mode 100644
index 0000000000..a980273b77
--- /dev/null
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html
@@ -0,0 +1,35 @@
+
+
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html
new file mode 100644
index 0000000000..d2b1b3be9c
--- /dev/null
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html
new file mode 100644
index 0000000000..3a5b1fbd43
--- /dev/null
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html
@@ -0,0 +1,36 @@
+
+
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html
index 9a66353059..0da4aa7c97 100644
--- a/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html
@@ -15,32 +15,6 @@
background: #0a0a0a;
font-family: Helvetica, Arial, sans-serif;
}
- .scene {
- position: absolute;
- inset: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0;
- }
- .title {
- font-size: 140px;
- font-weight: 800;
- color: #ffffff;
- letter-spacing: 0.05em;
- transform: scale(0);
- }
- .square {
- width: 200px;
- height: 200px;
- opacity: 0;
- transform: scale(0.8);
- }
- .outro {
- font-size: 100px;
- font-weight: 600;
- color: #ffffff;
- }
@@ -53,17 +27,35 @@
data-duration="6"
data-fps="30"
>
-
+
-
-
-
+
-
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json
index 82c0a7de71..f97159709a 100644
--- a/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json
@@ -12,9 +12,9 @@
"measured_mean_ssim": 0.953,
"measured_min_ssim": 0.927,
"measured_p05_ssim": 0.938,
- "measured_p95_ssim": 0.977,
- "measured_at": "2026-04-27",
- "measured_against": "remotion@4.0 (PNG output, BT.709) vs hyperframes@0.4.15-alpha.1",
+ "measured_p95_ssim": 0.978,
+ "measured_at": "2026-09-18",
+ "measured_against": "remotion@4.0 (PNG output, BT.709) vs hyperframes@0.7.71 (sub-composition fixtures)",
"notes": "Count-up timing in StatsScene shows a few-frame offset between Remotion's manual 1-(1-t)^3 and GSAP's power3.out — both formulas are identical, so the offset comes from sub-frame timing of when the seek + onUpdate fire. Final values converge correctly. Visible only as transient digit mismatches mid-animation; no SSIM impact above the noise floor."
},
"remotion_apis_exercised": [
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html
new file mode 100644
index 0000000000..323741f9f7
--- /dev/null
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html
@@ -0,0 +1,52 @@
+
+
+
+
+
STARGAZED
+
by HeyGen
+
+
+
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html
new file mode 100644
index 0000000000..ed456e69af
--- /dev/null
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html
@@ -0,0 +1,99 @@
+
+
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html
new file mode 100644
index 0000000000..c83b2d6f64
--- /dev/null
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
thanks for watching
+
+
+
+
+
+
diff --git a/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html
index e4253f2047..10156df88f 100644
--- a/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html
+++ b/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html
@@ -16,86 +16,6 @@
font-family: Helvetica, Arial, sans-serif;
color: #ffffff;
}
- .scene {
- position: absolute;
- inset: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0;
- }
-
- /* Scene 1 — Title */
- .scene-1 {
- flex-direction: column;
- }
- .scene-1 .title {
- font-size: 160px;
- font-weight: 900;
- letter-spacing: 0.05em;
- transform: scale(0);
- }
- .scene-1 .subtitle {
- font-size: 36px;
- font-weight: 400;
- color: #9ca3af;
- margin-top: 24px;
- opacity: 0;
- }
-
- /* Scene 2 — Stats */
- .scene-2 {
- gap: 48px;
- }
- .stat-card {
- width: 280px;
- height: 220px;
- background: #1a1a1a;
- border-radius: 16px;
- border: 2px solid var(--card-color);
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- opacity: 0;
- transform: scale(0);
- }
- .stat-card .number {
- font-size: 72px;
- font-weight: 800;
- color: var(--card-color);
- line-height: 1;
- }
- .stat-card .label {
- font-size: 24px;
- font-weight: 500;
- color: #9ca3af;
- margin-top: 16px;
- text-transform: uppercase;
- letter-spacing: 0.1em;
- }
-
- /* Scene 3 — Outro */
- .scene-3 .outro-wrap {
- position: relative;
- display: inline-block;
- opacity: 0;
- }
- .scene-3 .outro-text {
- font-size: 80px;
- font-weight: 600;
- }
- .scene-3 .outro-underline {
- position: absolute;
- left: 0;
- bottom: -8px;
- width: 100%;
- height: 6px;
- background: #fbbf24;
- border-radius: 3px;
- transform: scaleX(0);
- transform-origin: left center;
- }
@@ -111,158 +31,39 @@
data-subtitle="by HeyGen"
data-outro="thanks for watching"
>
-
-
STARGAZED
-
by HeyGen
-
+ >
-
+ >
-
-
-
thanks for watching
-
-
-
+ >
diff --git a/skills/remotion-to-hyperframes/references/api-map.md b/skills/remotion-to-hyperframes/references/api-map.md
index 08ce72f6c8..c6639889d9 100644
--- a/skills/remotion-to-hyperframes/references/api-map.md
+++ b/skills/remotion-to-hyperframes/references/api-map.md
@@ -27,12 +27,12 @@ details (timing, transitions, etc.).
See [sequencing.md](sequencing.md) for nesting and stagger details.
-| Remotion | HyperFrames |
-| ------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
-| `` | `` |
-| `
` + `` | siblings with sequential `data-start` values |
-| `` | not a primitive — emit a bounded GSAP repeat from the available duration |
-| `` | drop the wrapper; HF doesn't have running animation outside the seek-driven timeline so freeze is a no-op |
+| Remotion | HyperFrames |
+| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `` | a host `` in the root, its content in that sub-composition file (see [sequencing.md](sequencing.md)) |
+| `
` + `` | sibling scene hosts with sequential `data-start` values |
+| `` | not a primitive — emit a bounded GSAP repeat from the available duration |
+| `` | drop the wrapper; HF doesn't have running animation outside the seek-driven timeline so freeze is a no-op |
## Timing
@@ -53,16 +53,16 @@ See [timing.md](timing.md) — this is the highest-leverage section.
See [media.md](media.md) for trim, volume ramps, and decoder notes.
-| Remotion | HyperFrames |
-| -------------------------------------- | --------------------------------------------------------------------------- |
-| `` | `` |
-| `` | `data-playback-rate`, `data-trim-start`, `data-trim-end` |
-| `` | `` |
-| `` | `` — HF doesn't need the off-thread variant (uses headless Chrome) |
-| ` ` | ` ` |
-| `