Remotion to HyperFrames
Overview
Translate Remotion (React-based) video compositions into HyperFrames (HTML + GSAP) compositions. Most Remotion idioms have direct HyperFrames equivalents — the translation is mechanical for ~80% of typical compositions. This skill encodes the mapping and guards against the lossy 20% by refusing to translate patterns that don't fit HF's seek-driven model and recommending the runtime interop pattern from PR #214 instead.
The skill ships with a tiered test corpus (T1–T4, 4 fixtures total) that grades translations against measured SSIM thresholds. Don't translate without running the eval — a translation that "looks right" but renders 0.05 SSIM lower than the validated baseline is silently wrong.
Workflow
Step 1: Lint the source
Run scripts/lint_source.py over the Remotion source directory. The lint detects patterns that can't translate cleanly:
- Blockers (refuse + recommend interop):
useState, useReducer, useEffect/useLayoutEffect with non-empty deps, async calculateMetadata, third-party React UI libraries (MUI, Chakra, Mantine, antd, shadcn, Radix, NextUI).
- Warnings (translate after dropping the construct):
@remotion/lambda config, delayRender, useCallback, useMemo, custom hooks.
- Info (translate with note):
staticFile, interpolateColors.
If any blocker fires, stop. Read references/escape-hatch.md and surface the recommendation message. Warnings don't stop translation — drop the offending construct in step 3 and note the gap in TRANSLATION_NOTES.md. @remotion/lambda config is the canonical warning case: the skill drops the import + renderMediaOnLambda(...) calls but translates the rest of the composition.
Step 2: Plan the translation
Read references/api-map.md — the index of every Remotion API and its HF equivalent or per-topic reference. Identify which topic references you'll need based on what the source uses:
| Source contains |
Load reference |
Composition, defaultProps, schema, calculateMetadata |
parameters.md |
Sequence, Series, Loop, AbsoluteFill, Freeze |
sequencing.md |
useCurrentFrame, interpolate, spring, Easing, interpolateColors |
timing.md |
Audio, Video, Img, IFrame, staticFile, delayRender |
media.md |
TransitionSeries, @remotion/transitions |
transitions.md |
@remotion/lottie |
lottie.md |
@remotion/google-fonts/<Family>, Font.loadFont, @font-face |
fonts.md |
Don't load all of them — load only what the specific source needs.
Step 3: Generate the HF composition
Emit index.html with:
- Root
<div id="stage"> 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
<style> for layout; CSS sets the from state of every animated property.
- A single
<script> tag at the bottom containing one paused gsap.timeline({paused: true}). Every Remotion useCurrentFrame() derivation becomes a tween on this timeline at the right offset.
window.__timelines["<composition-id>"] = tl; registers the timeline with HF's runtime.
Custom React subcomponents inline as repeated HTML using the prop interface as the template (see parameters.md for the per-instance data-* pattern).
Step 4: Validate
Run the eval harness — references/eval.md for the full guide. Quick path:
# Render Remotion baseline (after npm install in the fixture)
cd remotion-src && npx remotion render <CompositionId> out/baseline.mp4
# Render HF translation
cd ../hf-src && npx hyperframes render --output ../hf.mp4
# SSIM diff
../../scripts/render_diff.sh ./remotion-src/out/baseline.mp4 ./hf.mp4 ./diff
Threshold: ~0.02 below p05 of the source's complexity tier (see eval.md's validated thresholds table). If the diff fails, run scripts/frame_strip.sh to see which frames diverged, then re-read the relevant timing/sequencing/media reference.
Critical: both renders must use matching pixel format. Set Config.setVideoImageFormat("png") + Config.setColorSpace("bt709") in the Remotion source's remotion.config.ts — otherwise the diff measures encoder differences (~0.05 SSIM hit), not translation fidelity.
Step 5: Document gaps
Anything that didn't translate cleanly (volume ramps dropped, custom presentations approximated, fonts substituted) gets a TRANSLATION_NOTES.md written next to the HF output. See references/limitations.md for the format.
What this skill explicitly does NOT do
- Translate React state machines. Compositions that drive animation via
useState + useEffect are not deterministic frame-capture targets in HyperFrames' seek-driven model. Recommend the runtime interop pattern.
- Run Remotion's render pipeline alongside HyperFrames. That's the runtime interop pattern from PR #214 — a separate solution for compositions that fail this skill's lint.
(@remotion/lambda is not a blocker — Lambda config is deployment, not animation. The skill drops it as a warning and translates the rest. See references/escape-hatch.md.)
How to grade your own translation
Run the test corpus orchestrator:
./assets/test-corpus/run.sh
It runs T1, T2, T3 (render + diff) and T4 (lint validation), prints a per-tier pass/fail table, and emits an aggregate JSON report. Use this to verify the skill is working end-to-end on a clean checkout — and as a regression check after editing any reference.
Validated baseline (as of 2026-04-27):
| Tier |
Composition shape |
Mean SSIM |
Threshold |
| T1 |
single-element fade-in |
0.974 |
0.95 |
| T2 |
multi-scene + spring + audio + image |
0.985 |
0.95 |
| T3 |
data-driven, custom subcomponents, count-up |
0.953 |
0.90 |
| T4 |
escape-hatch (8 lint cases) |
8/8 pass |
n/a |
1---2name: remotion-to-hyperframes3description: Translate a Remotion (React-based) video composition into a HyperFrames HTML composition. Use when (1) the user provides Remotion source (`.tsx` files using `useCurrentFrame`, `Sequence`, `AbsoluteFill`, `interpolate`, `spring`, `staticFile`, etc.) and asks to port, convert, or migrate it to HyperFrames; (2) the user pastes a Remotion entry point (`Root.tsx`, `Composition`) and wants HTML; (3) the user links a Remotion repo and asks for the HyperFrames equivalent; (4) the user says "port my Remotion project", "translate this Remotion code", "rewrite as HTML", or "I have a Remotion comp, make it HyperFrames". Skill detects unsupported patterns (useState, useEffect with side effects, async calculateMetadata, third-party React component libraries, `@remotion/lambda` features) and recommends the runtime interop escape hatch instead of attempting a lossy translation.4---56# Remotion to HyperFrames78## Overview910Translate Remotion (React-based) video compositions into HyperFrames (HTML + GSAP) compositions. Most Remotion idioms have direct HyperFrames equivalents — the translation is mechanical for ~80% of typical compositions. This skill encodes the mapping and guards against the lossy 20% by refusing to translate patterns that don't fit HF's seek-driven model and recommending the runtime interop pattern from [PR #214](https://github.com/heygen-com/hyperframes/pull/214) instead.1112The skill ships with a **tiered test corpus** (T1–T4, 4 fixtures total) that grades translations against measured SSIM thresholds. Don't translate without running the eval — a translation that "looks right" but renders 0.05 SSIM lower than the validated baseline is silently wrong.1314## Workflow1516### Step 1: Lint the source1718Run [`scripts/lint_source.py`](scripts/lint_source.py) over the Remotion source directory. The lint detects patterns that can't translate cleanly:1920- **Blockers** (refuse + recommend interop): `useState`, `useReducer`, `useEffect`/`useLayoutEffect` with non-empty deps, async `calculateMetadata`, third-party React UI libraries (MUI, Chakra, Mantine, antd, shadcn, Radix, NextUI).21- **Warnings** (translate after dropping the construct): `@remotion/lambda` config, `delayRender`, `useCallback`, `useMemo`, custom hooks.22- **Info** (translate with note): `staticFile`, `interpolateColors`.2324If any blocker fires, **stop**. Read [`references/escape-hatch.md`](references/escape-hatch.md) and surface the recommendation message. Warnings don't stop translation — drop the offending construct in step 3 and note the gap in `TRANSLATION_NOTES.md`. `@remotion/lambda` config is the canonical warning case: the skill drops the import + `renderMediaOnLambda(...)` calls but translates the rest of the composition.2526### Step 2: Plan the translation2728Read [`references/api-map.md`](references/api-map.md) — the index of every Remotion API and its HF equivalent or per-topic reference. Identify which topic references you'll need based on what the source uses:2930| Source contains | Load reference |31| ------------------------------------------------------------------------- | --------------------------------------------- |32| `Composition`, `defaultProps`, `schema`, `calculateMetadata` | [`parameters.md`](references/parameters.md) |33| `Sequence`, `Series`, `Loop`, `AbsoluteFill`, `Freeze` | [`sequencing.md`](references/sequencing.md) |34| `useCurrentFrame`, `interpolate`, `spring`, `Easing`, `interpolateColors` | [`timing.md`](references/timing.md) |35| `Audio`, `Video`, `Img`, `IFrame`, `staticFile`, `delayRender` | [`media.md`](references/media.md) |36| `TransitionSeries`, `@remotion/transitions` | [`transitions.md`](references/transitions.md) |37| `@remotion/lottie` | [`lottie.md`](references/lottie.md) |38| `@remotion/google-fonts/<Family>`, `Font.loadFont`, `@font-face` | [`fonts.md`](references/fonts.md) |3940Don't load all of them — load only what the specific source needs.4142### Step 3: Generate the HF composition4344Emit `index.html` with:4546- Root `<div id="stage">` 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.47- A flat list of scene divs with `data-start` / `data-duration` / `data-track-index`.48- Inline `<style>` for layout; CSS sets the `from` state of every animated property.49- A single `<script>` tag at the bottom containing one paused `gsap.timeline({paused: true})`. Every Remotion `useCurrentFrame()` derivation becomes a tween on this timeline at the right offset.50- `window.__timelines["<composition-id>"] = tl;` registers the timeline with HF's runtime.5152Custom React subcomponents inline as repeated HTML using the prop interface as the template (see [`parameters.md`](references/parameters.md) for the per-instance `data-*` pattern).5354### Step 4: Validate5556Run the eval harness — [`references/eval.md`](references/eval.md) for the full guide. Quick path:5758```bash59# Render Remotion baseline (after npm install in the fixture)60cd remotion-src && npx remotion render <CompositionId> out/baseline.mp46162# Render HF translation63cd ../hf-src && npx hyperframes render --output ../hf.mp46465# SSIM diff66../../scripts/render_diff.sh ./remotion-src/out/baseline.mp4 ./hf.mp4 ./diff67```6869Threshold: ~0.02 below `p05` of the source's complexity tier (see `eval.md`'s validated thresholds table). If the diff fails, run [`scripts/frame_strip.sh`](scripts/frame_strip.sh) to see _which_ frames diverged, then re-read the relevant timing/sequencing/media reference.7071**Critical**: both renders must use matching pixel format. Set `Config.setVideoImageFormat("png")` + `Config.setColorSpace("bt709")` in the Remotion source's `remotion.config.ts` — otherwise the diff measures encoder differences (~0.05 SSIM hit), not translation fidelity.7273### Step 5: Document gaps7475Anything that didn't translate cleanly (volume ramps dropped, custom presentations approximated, fonts substituted) gets a `TRANSLATION_NOTES.md` written next to the HF output. See [`references/limitations.md`](references/limitations.md) for the format.7677## What this skill explicitly does NOT do7879- **Translate React state machines.** Compositions that drive animation via `useState` + `useEffect` are not deterministic frame-capture targets in HyperFrames' seek-driven model. Recommend the runtime interop pattern.80- **Run Remotion's render pipeline alongside HyperFrames.** That's the runtime interop pattern from [PR #214](https://github.com/heygen-com/hyperframes/pull/214) — a separate solution for compositions that fail this skill's lint.8182(`@remotion/lambda` is _not_ a blocker — Lambda config is deployment, not animation. The skill drops it as a warning and translates the rest. See [`references/escape-hatch.md`](references/escape-hatch.md).)8384## How to grade your own translation8586Run the test corpus orchestrator:8788```bash89./assets/test-corpus/run.sh90```9192It runs T1, T2, T3 (render + diff) and T4 (lint validation), prints a per-tier pass/fail table, and emits an aggregate JSON report. Use this to verify the skill is working end-to-end on a clean checkout — and as a regression check after editing any reference.9394Validated baseline (as of 2026-04-27):9596| Tier | Composition shape | Mean SSIM | Threshold |97| ---- | ------------------------------------------- | --------- | --------- |98| T1 | single-element fade-in | 0.974 | 0.95 |99| T2 | multi-scene + spring + audio + image | 0.985 | 0.95 |100| T3 | data-driven, custom subcomponents, count-up | 0.953 | 0.90 |101| T4 | escape-hatch (8 lint cases) | 8/8 pass | n/a |