mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-08 02:36:10 +00:00
Adds the data-driven tier — a purpose-built fixture (option 2 from the stack discussion, not a port of PR #214's examples/remotion-full/) that exercises the realistic shape of a production Remotion composition without using the runtime adapter. Stargazed.tsx (10s @ 30fps, 1280x720): Sequence 0-3s TitleScene (title + subtitle) Sequence 3-7s StatsScene (3 reused StatCards staggered 12 frames apart) Sequence 7-10s OutroScene (UnderlinedText with scaleX-from-left underline) Composition shape exercises: - <Composition schema={z.object({...})} defaultProps={...} /> - nested array prop (stats[]) materialized as repeated HTML - custom React subcomponents (StatCard, AnimatedNumber, UnderlinedText) reused with different props - per-instance delay via prop (delayInFrames -> GSAP timeline offset) - frame-driven count-up (AnimatedNumber, manual cubic ease-out) - two different spring configs in the same composition (damping:12 -> back.out(1.4), damping:14 -> back.out(1.2)) - useCurrentFrame, useVideoConfig Translation choices documented in README.md and expected.json: - Zod props -> data-* on root #stage div - Custom subcomponents inline as repeated HTML using prop interface as the template - AnimatedNumber's frame-driven count-up -> GSAP onUpdate tween on a { v: 0 } counter object, ease power3.out - Two different spring configs -> two different back.out overshoots (1.4 vs 1.2 approximates the damping difference) - delayInFrames={i * 12} -> GSAP offset (i * 0.4)s Validated end-to-end: rendered Remotion baseline + HF translation, ran scripts/render_diff.sh. measured mean SSIM 0.953 measured min SSIM 0.927 measured p05 SSIM 0.938 threshold 0.90 (~0.04 below p05) The wider gap vs T1/T2 reflects T3's bigger approximation budget (2 spring instances + count-up timing + font fallback on multiple text sizes). Mean SSIM below 0.90 = structural mismatch (wrong durations, wrong stagger, missing prop wiring), not approximation drift. Same Remotion config as PR 3: setVideoImageFormat("png") + setColorSpace("bt709") to match HF's yuv420p output. Lint: 9 files scanned, 0 blockers / 0 warnings / 0 infos. oxlint, oxfmt, typecheck all pass. The fixture is not yet wired into CI; render + diff is documented in README.md and runs by hand via the harness from PR 2. PR 7's orchestrator will wire all four tiers into a CI eval run.
42 lines
3.4 KiB
JSON
42 lines
3.4 KiB
JSON
{
|
||
"tier": 3,
|
||
"name": "stargazed-data-driven",
|
||
"composition_id": "Stargazed",
|
||
"description": "Data-driven 10-second composition with three scenes, custom React subcomponents reused across scenes, a Zod schema with defaultProps, and a count-up number animation. Translates the realistic shape of a production Remotion composition into HF — without using the runtime adapter from PR #214.",
|
||
"duration_seconds": 10,
|
||
"fps": 30,
|
||
"width": 1280,
|
||
"height": 720,
|
||
"ssim_threshold": 0.9,
|
||
"validation": {
|
||
"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",
|
||
"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": [
|
||
"Composition with z.object schema and typed defaultProps",
|
||
"Sequence (3 nested with computed offsets)",
|
||
"AbsoluteFill",
|
||
"useCurrentFrame, useVideoConfig",
|
||
"interpolate (single-segment, multi-segment, with extrapolation)",
|
||
"spring (two configs: damping:12 and damping:14)",
|
||
"Custom React subcomponents reused with different props (StatCard ×3)",
|
||
"Custom React utility component (AnimatedNumber for count-up)",
|
||
"Custom React utility component (UnderlinedText)",
|
||
"Per-instance delay via prop (delayInFrames)"
|
||
],
|
||
"translation_notes": [
|
||
"Zod schema + defaultProps → data-* attributes on the root #stage div. The skill emits one data attribute per scalar prop; nested arrays (stats[]) get materialized as repeated HTML markup with per-instance data attributes (data-stat-index, data-stat-value, --card-color).",
|
||
"Custom React subcomponents inline as repeated HTML divs. The component prop interface becomes the repeated markup template. This is lossy for components with internal state — fine here because StatCard, AnimatedNumber, UnderlinedText all derive from props alone.",
|
||
"AnimatedNumber's frame-driven count-up → a GSAP tween on a { v: 0 } counter object with onUpdate rewriting textContent. GSAP's power3.out is cubic easeOut, matching the Remotion 1-(1-t)^3 manual ease.",
|
||
"Two different springs in this composition: damping:12 → back.out(1.4) (snappy), damping:14 → back.out(1.2) (calmer). The 1.4 vs 1.2 overshoot ratio approximates the damping difference.",
|
||
"Per-instance stagger via delayInFrames prop translates to a GSAP timeline offset of (i * 0.4)s.",
|
||
"Threshold 0.90 reflects: spring → back.out approximation (×2 different configs), the count-up easing curve match (very close but not identical due to sub-frame seek timing), font/AA differences on body text. SSIM well below 0.90 indicates a structural mismatch, not approximation drift."
|
||
],
|
||
"rationale": "Threshold 0.90 sits ~0.04 below measured p05 (0.938). The wider gap vs T1/T2 reflects T3's bigger approximation budget (2 spring instances + count-up timing + font fallback on multiple text sizes). Mean SSIM below 0.90 = structural mismatch (wrong durations, wrong stagger, missing prop wiring), not approximation drift."
|
||
}
|