From 4575654f8d726b52e1211ea36f63555b869fc517 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 02:23:15 +0000 Subject: [PATCH 1/2] Add heygen-com/hyperframes skills and lock file https://claude.ai/code/session_0166DeGxQLDHSm3f7Ld5psQA --- .agents/skills/animejs/SKILL.md | 114 ++ .agents/skills/contribute-catalog/SKILL.md | 211 +++ .../skills/contribute-catalog/templates.md | 417 +++++ .agents/skills/css-animations/SKILL.md | 124 ++ .agents/skills/gsap/SKILL.md | 240 +++ .agents/skills/gsap/references/effects.md | 297 ++++ .../skills/gsap/scripts/extract-audio-data.py | 188 +++ .agents/skills/hyperframes-cli/SKILL.md | 144 ++ .agents/skills/hyperframes-media/SKILL.md | 243 +++ .agents/skills/hyperframes-registry/SKILL.md | 104 ++ .../examples/add-block.md | 51 + .../examples/add-component.md | 73 + .../references/demo-html-pattern.md | 54 + .../references/discovery.md | 53 + .../references/install-locations.md | 45 + .../references/wiring-blocks.md | 91 ++ .../references/wiring-components.md | 77 + .agents/skills/hyperframes/SKILL.md | 491 ++++++ .agents/skills/hyperframes/data-in-motion.md | 19 + .agents/skills/hyperframes/house-style.md | 73 + .../hyperframes/palettes/bold-energetic.md | 14 + .../hyperframes/palettes/clean-corporate.md | 14 + .../hyperframes/palettes/dark-premium.md | 14 + .../skills/hyperframes/palettes/jewel-rich.md | 14 + .../skills/hyperframes/palettes/monochrome.md | 14 + .../hyperframes/palettes/nature-earth.md | 14 + .../hyperframes/palettes/neon-electric.md | 14 + .../hyperframes/palettes/pastel-soft.md | 14 + .../hyperframes/palettes/warm-editorial.md | 14 + .agents/skills/hyperframes/patterns.md | 191 +++ .../hyperframes/references/audio-reactive.md | 76 + .../hyperframes/references/beat-direction.md | 171 ++ .../skills/hyperframes/references/captions.md | 163 ++ .../hyperframes/references/css-patterns.md | 373 +++++ .../hyperframes/references/design-picker.md | 117 ++ .../references/dynamic-techniques.md | 102 ++ .../references/html-in-canvas-patterns.md | 507 ++++++ .../references/motion-principles.md | 150 ++ .../hyperframes/references/narration.md | 92 ++ .../references/prompt-expansion.md | 68 + .../hyperframes/references/techniques.md | 525 ++++++ .../hyperframes/references/text-effects.md | 64 + .../references/transcript-guide.md | 107 ++ .../hyperframes/references/transitions.md | 138 ++ .../references/transitions/catalog.md | 117 ++ .../references/transitions/css-3d.md | 12 + .../references/transitions/css-blur.md | 51 + .../references/transitions/css-cover.md | 43 + .../references/transitions/css-destruction.md | 95 ++ .../references/transitions/css-dissolve.md | 66 + .../references/transitions/css-distortion.md | 45 + .../references/transitions/css-grid.md | 10 + .../references/transitions/css-light.md | 49 + .../references/transitions/css-mechanical.md | 30 + .../references/transitions/css-other.md | 25 + .../references/transitions/css-push.md | 41 + .../references/transitions/css-radial.md | 37 + .../references/transitions/css-scale.md | 24 + .../hyperframes/references/typography.md | 175 ++ .../references/video-composition.md | 62 + .../hyperframes/scripts/animation-map.mjs | 601 +++++++ .../hyperframes/scripts/contrast-report.mjs | 348 ++++ .../hyperframes/scripts/package-loader.mjs | 269 ++++ .../hyperframes/templates/design-picker.html | 1432 +++++++++++++++++ .agents/skills/hyperframes/visual-styles.md | 443 +++++ .agents/skills/lottie/SKILL.md | 112 ++ .../skills/remotion-to-hyperframes/SKILL.md | 120 ++ .../remotion-to-hyperframes/assets/.gitkeep | 0 .../assets/test-corpus/.gitignore | 1 + .../assets/test-corpus/run.sh | 249 +++ .../test-corpus/tier-1-title-card/.gitignore | 10 + .../test-corpus/tier-1-title-card/README.md | 41 + .../tier-1-title-card/expected.json | 26 + .../tier-1-title-card/hf-src/index.html | 61 + .../remotion-src/package.json | 14 + .../remotion-src/remotion.config.ts | 13 + .../remotion-src/src/Root.tsx | 13 + .../remotion-src/src/TitleCard.tsx | 34 + .../remotion-src/src/index.ts | 4 + .../remotion-src/tsconfig.json | 15 + .../test-corpus/tier-2-multi-scene/.gitignore | 14 + .../test-corpus/tier-2-multi-scene/README.md | 54 + .../tier-2-multi-scene/expected.json | 29 + .../tier-2-multi-scene/hf-src/index.html | 118 ++ .../remotion-src/package.json | 14 + .../remotion-src/remotion.config.ts | 13 + .../remotion-src/src/MultiScene.tsx | 105 ++ .../remotion-src/src/Root.tsx | 13 + .../remotion-src/src/index.ts | 4 + .../remotion-src/tsconfig.json | 15 + .../test-corpus/tier-2-multi-scene/setup.sh | 34 + .../test-corpus/tier-3-data-driven/.gitignore | 10 + .../test-corpus/tier-3-data-driven/README.md | 85 + .../tier-3-data-driven/expected.json | 41 + .../tier-3-data-driven/hf-src/index.html | 269 ++++ .../remotion-src/package.json | 15 + .../remotion-src/remotion.config.ts | 13 + .../remotion-src/src/Root.tsx | 27 + .../remotion-src/src/Stargazed.tsx | 37 + .../src/components/AnimatedNumber.tsx | 23 + .../remotion-src/src/components/StatCard.tsx | 59 + .../src/components/UnderlinedText.tsx | 47 + .../remotion-src/src/index.ts | 4 + .../remotion-src/src/scenes/OutroScene.tsx | 18 + .../remotion-src/src/scenes/StatsScene.tsx | 34 + .../remotion-src/src/scenes/TitleScene.tsx | 55 + .../remotion-src/tsconfig.json | 15 + .../test-corpus/tier-4-escape-hatch/README.md | 51 + .../cases/01-use-state.tsx | 29 + .../cases/02-use-effect-deps.tsx | 29 + .../cases/03-async-metadata.tsx | 39 + .../cases/04-third-party-react.tsx | 30 + .../cases/05-lambda-config.tsx | 38 + .../cases/06-warnings-only.tsx | 36 + .../cases/07-custom-hook.tsx | 28 + .../tier-4-escape-hatch/cases/08-mixed.tsx | 41 + .../tier-4-escape-hatch/expected.json | 85 + .../tier-4-escape-hatch/validate.sh | 110 ++ .../references/api-map.md | 142 ++ .../references/escape-hatch.md | 115 ++ .../references/eval.md | 140 ++ .../references/fonts.md | 112 ++ .../references/limitations.md | 136 ++ .../references/lottie.md | 121 ++ .../references/media.md | 149 ++ .../references/parameters.md | 167 ++ .../references/sequencing.md | 195 +++ .../references/timing.md | 165 ++ .../references/transitions.md | 114 ++ .../remotion-to-hyperframes/scripts/.gitkeep | 0 .../scripts/frame_strip.sh | 107 ++ .../scripts/lint_source.py | 358 +++++ .../scripts/render_diff.sh | 103 ++ .../scripts/tests/fixtures/blocker.tsx | 50 + .../scripts/tests/fixtures/clean.tsx | 46 + .../scripts/tests/smoke.sh | 90 ++ .agents/skills/tailwind/SKILL.md | 148 ++ .agents/skills/three/SKILL.md | 106 ++ .agents/skills/typegpu/SKILL.md | 174 ++ .agents/skills/waapi/SKILL.md | 94 ++ .../skills/website-to-hyperframes/SKILL.md | 140 ++ .../assets/sfx/CREDITS.md | 35 + .../assets/sfx/chime.mp3 | Bin 0 -> 27309 bytes .../assets/sfx/click-soft.mp3 | Bin 0 -> 11702 bytes .../assets/sfx/click.mp3 | Bin 0 -> 11702 bytes .../assets/sfx/error.mp3 | Bin 0 -> 51827 bytes .../assets/sfx/glitch-1.mp3 | Bin 0 -> 84427 bytes .../assets/sfx/glitch-2.mp3 | Bin 0 -> 112128 bytes .../assets/sfx/glitch-3.mp3 | Bin 0 -> 99072 bytes .../assets/sfx/impact-bass-1.mp3 | Bin 0 -> 67709 bytes .../assets/sfx/impact-bass-2.mp3 | Bin 0 -> 82944 bytes .../assets/sfx/key-press.mp3 | Bin 0 -> 3909 bytes .../assets/sfx/manifest.json | 97 ++ .../assets/sfx/notification.mp3 | Bin 0 -> 78576 bytes .../assets/sfx/ping.mp3 | Bin 0 -> 26400 bytes .../website-to-hyperframes/assets/sfx/pop.mp3 | Bin 0 -> 23040 bytes .../assets/sfx/riser.mp3 | Bin 0 -> 321024 bytes .../assets/sfx/sparkle.mp3 | Bin 0 -> 57678 bytes .../assets/sfx/typing.mp3 | Bin 0 -> 26852 bytes .../assets/sfx/whoosh-cinematic.mp3 | Bin 0 -> 177408 bytes .../assets/sfx/whoosh-short.mp3 | Bin 0 -> 18390 bytes .../assets/sfx/whoosh.mp3 | Bin 0 -> 18390 bytes .../references/beat-builder-guide.md | 263 +++ .../references/capabilities.md | 713 ++++++++ .../references/step-0-capture.md | 55 + .../references/step-1-design.md | 333 ++++ .../references/step-2-brief.md | 178 ++ .../references/step-3-storyboard.md | 572 +++++++ .../references/step-4-vo.md | 230 +++ .../references/step-5-build.md | 483 ++++++ .../references/step-6-validate.md | 341 ++++ .../scripts/w2h-verify.mjs | 759 +++++++++ skills-lock.json | 95 ++ 173 files changed, 19574 insertions(+) create mode 100644 .agents/skills/animejs/SKILL.md create mode 100644 .agents/skills/contribute-catalog/SKILL.md create mode 100644 .agents/skills/contribute-catalog/templates.md create mode 100644 .agents/skills/css-animations/SKILL.md create mode 100644 .agents/skills/gsap/SKILL.md create mode 100644 .agents/skills/gsap/references/effects.md create mode 100644 .agents/skills/gsap/scripts/extract-audio-data.py create mode 100644 .agents/skills/hyperframes-cli/SKILL.md create mode 100644 .agents/skills/hyperframes-media/SKILL.md create mode 100644 .agents/skills/hyperframes-registry/SKILL.md create mode 100644 .agents/skills/hyperframes-registry/examples/add-block.md create mode 100644 .agents/skills/hyperframes-registry/examples/add-component.md create mode 100644 .agents/skills/hyperframes-registry/references/demo-html-pattern.md create mode 100644 .agents/skills/hyperframes-registry/references/discovery.md create mode 100644 .agents/skills/hyperframes-registry/references/install-locations.md create mode 100644 .agents/skills/hyperframes-registry/references/wiring-blocks.md create mode 100644 .agents/skills/hyperframes-registry/references/wiring-components.md create mode 100644 .agents/skills/hyperframes/SKILL.md create mode 100644 .agents/skills/hyperframes/data-in-motion.md create mode 100644 .agents/skills/hyperframes/house-style.md create mode 100644 .agents/skills/hyperframes/palettes/bold-energetic.md create mode 100644 .agents/skills/hyperframes/palettes/clean-corporate.md create mode 100644 .agents/skills/hyperframes/palettes/dark-premium.md create mode 100644 .agents/skills/hyperframes/palettes/jewel-rich.md create mode 100644 .agents/skills/hyperframes/palettes/monochrome.md create mode 100644 .agents/skills/hyperframes/palettes/nature-earth.md create mode 100644 .agents/skills/hyperframes/palettes/neon-electric.md create mode 100644 .agents/skills/hyperframes/palettes/pastel-soft.md create mode 100644 .agents/skills/hyperframes/palettes/warm-editorial.md create mode 100644 .agents/skills/hyperframes/patterns.md create mode 100644 .agents/skills/hyperframes/references/audio-reactive.md create mode 100644 .agents/skills/hyperframes/references/beat-direction.md create mode 100644 .agents/skills/hyperframes/references/captions.md create mode 100644 .agents/skills/hyperframes/references/css-patterns.md create mode 100644 .agents/skills/hyperframes/references/design-picker.md create mode 100644 .agents/skills/hyperframes/references/dynamic-techniques.md create mode 100644 .agents/skills/hyperframes/references/html-in-canvas-patterns.md create mode 100644 .agents/skills/hyperframes/references/motion-principles.md create mode 100644 .agents/skills/hyperframes/references/narration.md create mode 100644 .agents/skills/hyperframes/references/prompt-expansion.md create mode 100644 .agents/skills/hyperframes/references/techniques.md create mode 100644 .agents/skills/hyperframes/references/text-effects.md create mode 100644 .agents/skills/hyperframes/references/transcript-guide.md create mode 100644 .agents/skills/hyperframes/references/transitions.md create mode 100644 .agents/skills/hyperframes/references/transitions/catalog.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-3d.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-blur.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-cover.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-destruction.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-dissolve.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-distortion.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-grid.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-light.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-mechanical.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-other.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-push.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-radial.md create mode 100644 .agents/skills/hyperframes/references/transitions/css-scale.md create mode 100644 .agents/skills/hyperframes/references/typography.md create mode 100644 .agents/skills/hyperframes/references/video-composition.md create mode 100644 .agents/skills/hyperframes/scripts/animation-map.mjs create mode 100644 .agents/skills/hyperframes/scripts/contrast-report.mjs create mode 100644 .agents/skills/hyperframes/scripts/package-loader.mjs create mode 100644 .agents/skills/hyperframes/templates/design-picker.html create mode 100644 .agents/skills/hyperframes/visual-styles.md create mode 100644 .agents/skills/lottie/SKILL.md create mode 100644 .agents/skills/remotion-to-hyperframes/SKILL.md create mode 100644 .agents/skills/remotion-to-hyperframes/assets/.gitkeep create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/.gitignore create mode 100755 .agents/skills/remotion-to-hyperframes/assets/test-corpus/run.sh create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/.gitignore create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/README.md create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/package.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/remotion.config.ts create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/Root.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/TitleCard.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/index.ts create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/tsconfig.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/.gitignore create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/package.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/remotion.config.ts create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/MultiScene.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/Root.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/index.ts create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/tsconfig.json create mode 100755 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/setup.sh create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/.gitignore create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/README.md create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/package.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/remotion.config.ts create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Root.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Stargazed.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/AnimatedNumber.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/StatCard.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/UnderlinedText.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/index.ts create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/OutroScene.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/StatsScene.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/TitleScene.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/tsconfig.json create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/README.md create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/01-use-state.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/02-use-effect-deps.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/03-async-metadata.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/04-third-party-react.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/05-lambda-config.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/06-warnings-only.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/07-custom-hook.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/08-mixed.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/expected.json create mode 100755 .agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/validate.sh create mode 100644 .agents/skills/remotion-to-hyperframes/references/api-map.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/escape-hatch.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/eval.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/fonts.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/limitations.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/lottie.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/media.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/parameters.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/sequencing.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/timing.md create mode 100644 .agents/skills/remotion-to-hyperframes/references/transitions.md create mode 100644 .agents/skills/remotion-to-hyperframes/scripts/.gitkeep create mode 100755 .agents/skills/remotion-to-hyperframes/scripts/frame_strip.sh create mode 100755 .agents/skills/remotion-to-hyperframes/scripts/lint_source.py create mode 100755 .agents/skills/remotion-to-hyperframes/scripts/render_diff.sh create mode 100644 .agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/blocker.tsx create mode 100644 .agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/clean.tsx create mode 100755 .agents/skills/remotion-to-hyperframes/scripts/tests/smoke.sh create mode 100644 .agents/skills/tailwind/SKILL.md create mode 100644 .agents/skills/three/SKILL.md create mode 100644 .agents/skills/typegpu/SKILL.md create mode 100644 .agents/skills/waapi/SKILL.md create mode 100644 .agents/skills/website-to-hyperframes/SKILL.md create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/CREDITS.md create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/chime.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/click-soft.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/click.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/error.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/glitch-1.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/glitch-2.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/glitch-3.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/impact-bass-1.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/impact-bass-2.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/key-press.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/manifest.json create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/notification.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/ping.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/pop.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/riser.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/sparkle.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/typing.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/whoosh-cinematic.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/whoosh-short.mp3 create mode 100644 .agents/skills/website-to-hyperframes/assets/sfx/whoosh.mp3 create mode 100644 .agents/skills/website-to-hyperframes/references/beat-builder-guide.md create mode 100644 .agents/skills/website-to-hyperframes/references/capabilities.md create mode 100644 .agents/skills/website-to-hyperframes/references/step-0-capture.md create mode 100644 .agents/skills/website-to-hyperframes/references/step-1-design.md create mode 100644 .agents/skills/website-to-hyperframes/references/step-2-brief.md create mode 100644 .agents/skills/website-to-hyperframes/references/step-3-storyboard.md create mode 100644 .agents/skills/website-to-hyperframes/references/step-4-vo.md create mode 100644 .agents/skills/website-to-hyperframes/references/step-5-build.md create mode 100644 .agents/skills/website-to-hyperframes/references/step-6-validate.md create mode 100644 .agents/skills/website-to-hyperframes/scripts/w2h-verify.mjs create mode 100644 skills-lock.json diff --git a/.agents/skills/animejs/SKILL.md b/.agents/skills/animejs/SKILL.md new file mode 100644 index 0000000..9eeea2b --- /dev/null +++ b/.agents/skills/animejs/SKILL.md @@ -0,0 +1,114 @@ +--- +name: animejs +description: Anime.js adapter patterns for HyperFrames. Use when writing Anime.js animations or timelines inside HyperFrames compositions, registering animations on window.__hfAnime, making Anime.js seek-driven and deterministic, or translating Anime.js examples into render-safe HyperFrames HTML. +--- + +# Anime.js for HyperFrames + +HyperFrames can seek Anime.js instances through its `animejs` runtime adapter. The composition owns the animation objects; HyperFrames owns the clock. + +## Contract + +- Create animations or timelines synchronously during composition initialization. +- Set `autoplay: false` so Anime.js does not advance on its own clock. +- Register every returned animation or timeline on `window.__hfAnime`. +- Use finite durations and loop counts. +- Avoid callbacks that mutate DOM based on wall-clock time, network state, or unseeded randomness. + +The adapter seeks every registered instance with `instance.seek(timeMs)`, where `timeMs` is HyperFrames time in milliseconds. + +## Basic Pattern + +```html + + +``` + +## Timeline Pattern + +```html + +``` + +## Module Builds + +If you use an ES module build, the adapter does not care how the instance was created. It only needs the returned object to expose `seek()`, `pause()`, and preferably `play()`: + +```html + +``` + +## Good Uses + +- Small SVG and DOM flourishes where Anime.js syntax is compact. +- Imported Anime.js examples that can be made seek-driven. +- Multiple independent micro-animations pushed into the same registry. + +Use GSAP for complex scene sequencing unless the user specifically asks for Anime.js. GSAP is still the primary HyperFrames authoring path. + +## Avoid + +- Leaving `autoplay` at the Anime.js default. +- Depending on `anime.running` auto-discovery instead of explicit `window.__hfAnime.push(...)`. +- Infinite loops. Compute a finite repeat count from the composition duration. +- Building animations in timers, promises, event handlers, or after async asset loads. + +## Validation + +After editing a composition that uses Anime.js: + +```bash +npx hyperframes lint +npx hyperframes validate +``` + +## Credits And References + +- HyperFrames adapter source: `packages/core/src/runtime/adapters/animejs.ts`. +- Anime.js documentation for `autoplay`, `pause()`, and `seek()`: https://animejs.com/documentation/ diff --git a/.agents/skills/contribute-catalog/SKILL.md b/.agents/skills/contribute-catalog/SKILL.md new file mode 100644 index 0000000..db570cf --- /dev/null +++ b/.agents/skills/contribute-catalog/SKILL.md @@ -0,0 +1,211 @@ +--- +name: contribute-catalog +description: Author a new HyperFrames registry block (caption style, VFX block, transition, lower third) or component (text effect, overlay, snippet) and ship it as an upstream PR to the hyperframes repo. Use ONLY when the user wants to CONTRIBUTE to the public catalog — for in-project caption/transition authoring use the `hyperframes` skill, for installing existing registry items use the `hyperframes-registry` skill. +--- + +# Contribute to HyperFrames Registry + +Guide the user from idea to merged PR for a new registry block or component. + +## Workflow + +``` +1. Clarify → 2. Scaffold → 3. Build → 4. Validate → 5. Preview → 6. Ship +``` + +### Step 1: Clarify + +Ask what they're building. The registry has two item types: + +- **Block** (`registry/blocks/`, type `hyperframes:block`) — a full standalone composition with fixed dimensions and duration. Caption styles, VFX effects, title cards, lower thirds. +- **Component** (`registry/components/`, type `hyperframes:component`) — a reusable snippet with no fixed dimensions or duration. CSS effects, text treatments, overlays that adapt to any composition size. + +Then ask: + +- One-sentence description of the effect +- Visual reference (URL, screenshot, or description) +- Who uses this and when? + +### Step 2: Scaffold + +Create the registry structure: + +**For blocks:** + +``` +registry/blocks/{block-name}/ + {block-name}.html + registry-item.json +``` + +**For components:** + +``` +registry/components/{component-name}/ + {component-name}.html + registry-item.json +``` + +**Naming convention:** + +| Item name | ID prefix | Example IDs | +| ---------------- | --------- | ---------------------- | +| `cap-hormozi` | `hz` | `hz-cg-0`, `hz-cw-3` | +| `cap-typewriter` | `tw` | `tw-cg-0`, `tw-ch-0-5` | +| `vfx-chrome` | `vc` | `vc-canvas` | + +Use a 2-3 letter prefix. ALL element IDs must use this prefix to avoid collisions in sub-compositions. + +**registry-item.json for blocks:** + +```json +{ + "$schema": "https://hyperframes.heygen.com/schema/registry-item.json", + "name": "{block-name}", + "type": "hyperframes:block", + "title": "{Human Title}", + "description": "{one sentence}", + "dimensions": { "width": 1920, "height": 1080 }, // adjust: 1080x1920 for portrait/social + "duration": 10, // adjust for your composition + "tags": ["{category}", "{subcategory}"], + "files": [ + { + "path": "{block-name}.html", + "target": "compositions/{block-name}.html", + "type": "hyperframes:composition" + } + ] +} +``` + +**registry-item.json for components** (no `dimensions` or `duration`): + +```json +{ + "$schema": "https://hyperframes.heygen.com/schema/registry-item.json", + "name": "{component-name}", + "type": "hyperframes:component", + "title": "{Human Title}", + "description": "{one sentence}", + "tags": ["{category}"], + "files": [ + { + "path": "{component-name}.html", + "target": "compositions/components/{component-name}.html", + "type": "hyperframes:snippet" + } + ] +} +``` + +### Step 3: Build + +Apply the correct template based on type. See [templates.md](templates.md) for copy-paste starters. + +#### Caption blocks + +**Non-negotiable caption rules:** + +- Font: **96px minimum** for proportional fonts. **64-72px acceptable for monospace** (wider characters need less size). +- Readability: `-webkit-text-stroke: 2-3px` OR multi-layer `text-shadow` +- Overflow: call `window.__hyperframes.fitTextFontSize()` on every group +- Karaoke: highlight active word via `tl.to(wordEl, { color/scale }, WORDS[wi].start)` +- Hard kill: `tl.set(groupEl, { opacity: 0, visibility: "hidden" }, g.end)` on EVERY group +- **Never use `tl.from(el, { opacity: 0 })` at the same position as `tl.set(el, { opacity: 1 })`** — the from clobbers the set. Use `tl.to` instead. + +**Per-character animation** (typewriter, scramble): + +- Wrap each character in `` with ID `{prefix}-ch-{group}-{char}` +- Stagger via `tl.set` at computed intervals from word timestamps +- Cursors/decorative elements: use `tl.set` at intervals — NOT CSS animation (not seekable) + +**Positioning variants:** + +- Centered: `display: flex; align-items: center; justify-content: center;` +- Lower-third: `position: absolute; bottom: 100px; left: 0; width: 100%; text-align: center;` +- Left-aligned: `position: absolute; bottom: 100px; left: 120px; text-align: left;` + +#### VFX blocks (Three.js) + +- Use `three@0.147.0` from CDN (global script) +- `tl.eventCallback("onUpdate", renderScene); renderScene();` — NO requestAnimationFrame +- State proxy pattern: GSAP animates plain JS object, render function reads it +- Seeded PRNG (`mulberry32`) for randomness + +#### All types + +- `data-composition-id` MUST match `window.__timelines["id"]` +- All element IDs prefixed with block abbreviation +- `gsap.timeline({ paused: true })` — always paused +- No `Math.random()`, no `Date.now()` + +### Step 4: Validate + +```bash +hyperframes lint # 0 errors required +hyperframes validate --no-contrast # 0 console errors required +``` + +### Step 5: Preview + +```bash +# Render preview video +hyperframes render -o preview.mp4 + +# Snapshot for visual QA +hyperframes snapshot --at "1.0,3.0,5.0,7.0" + +# Publish to hyperframes.dev for review +npx hyperframes publish +``` + +**Catalog preview image** — The catalog card uses a PNG at `docs/images/catalog/{kind}/{name}.png` (where `{kind}` is `blocks` or `components`). Generate it from a snapshot, then: + +- **HeyGen internal contributors:** run `scripts/upload-docs-images.sh` (requires AWS profile `engineering-767398024897`) +- **External contributors:** attach the preview MP4 to your PR description. A maintainer will generate and upload the catalog image before merging. + +### Step 6: Ship + +**All steps are required. Missing any one produces a broken catalog entry.** + +`{kind}` is `blocks` or `components` depending on what you built in Step 1. + +```bash +# 1. Create branch +git checkout -b feat/registry-{name} + +# 2. Format HTML +npx oxfmt registry/{kind}/{name}/*.html + +# 3. Update registry/registry.json — add entry to the "items" array: +# { "name": "{name}", "type": "hyperframes:block" } (or "hyperframes:component") + +# 4. Generate catalog docs page +npx tsx scripts/generate-catalog-pages.ts + +# 5. Publish to hyperframes.dev so reviewers can preview +npx hyperframes publish + +# 6. Stage everything +git add registry/{kind}/{name}/ registry/registry.json docs/catalog/ + +# 7. Commit +git commit -m "feat(registry): add {name} — {one sentence}" + +# 8. Push and open PR with hyperframes.dev link +git push origin feat/registry-{name} +gh pr create --title "feat(registry): {name}" --body "preview: {hyperframes.dev-url}" +``` + +**If you don't have a GitHub account:** you need one to open a PR. Sign up at https://github.com/signup, then run `gh auth login`. + +## Quality Gate + +- [ ] `hyperframes lint` → 0 errors +- [ ] `hyperframes validate` → 0 console errors +- [ ] `npx oxfmt --check` passes +- [ ] `registry/registry.json` updated with new entry +- [ ] `scripts/generate-catalog-pages.ts` run (docs page generated) +- [ ] `npx hyperframes publish` run (claim your project URL) +- [ ] Preview MP4 attached to PR (external) or catalog PNG uploaded (internal) +- [ ] All IDs unique and prefixed diff --git a/.agents/skills/contribute-catalog/templates.md b/.agents/skills/contribute-catalog/templates.md new file mode 100644 index 0000000..84d104e --- /dev/null +++ b/.agents/skills/contribute-catalog/templates.md @@ -0,0 +1,417 @@ +# Contribute Templates + +Copy-paste starter templates for each component type. These embed the proven patterns that pass lint and validate. + +## Caption Template + +```html + + + + + + + + + +
+
+
+
+ + + +``` + +**Replace checklist:** + +- `BLOCKNAME` → your block name (e.g., `cap-swoosh`) +- `PREFIX` → short unique prefix for IDs (e.g., `sw`) +- Font family, weight, size → your style's typography +- Entrance animation → your style's entrance +- Karaoke highlight → your style's active word treatment +- Colors → your style's palette + +--- + +## VFX Template + +```html + + + + + + + + + +
+ +
+
+ + + +``` + +**Replace checklist:** + +- `BLOCKNAME` → your block name (e.g., `vfx-chrome-blob`) +- Scene setup → your geometry, lights, materials +- State proxy → your animated properties +- Tweens → your animation timeline +- renderScene → apply state to your Three.js objects + +--- + +## registry-item.json Templates + +**For blocks:** + +```json +{ + "$schema": "https://hyperframes.heygen.com/schema/registry-item.json", + "name": "BLOCKNAME", + "type": "hyperframes:block", + "title": "Human-Readable Title", + "description": "One sentence: what it does and who uses it", + "dimensions": { "width": 1920, "height": 1080 }, + "duration": 10, + "tags": ["category", "subcategory"], + "files": [ + { + "path": "BLOCKNAME.html", + "target": "compositions/BLOCKNAME.html", + "type": "hyperframes:composition" + } + ] +} +``` + +**For components** (no `dimensions` or `duration`): + +```json +{ + "$schema": "https://hyperframes.heygen.com/schema/registry-item.json", + "name": "COMPONENTNAME", + "type": "hyperframes:component", + "title": "Human-Readable Title", + "description": "One sentence: what it does", + "tags": ["category"], + "files": [ + { + "path": "COMPONENTNAME.html", + "target": "compositions/components/COMPONENTNAME.html", + "type": "hyperframes:snippet" + } + ] +} +``` + +Tags by category: + +- Captions: `captions`, `viral`, `professional`, `karaoke`, `minimal` +- VFX: `three-js`, `particles`, `shader`, `gpu` +- Transitions: `transition`, `shader`, `wipe`, `dissolve` +- Blocks: `lower-third`, `social`, `title-card`, `data-viz` +- Components: `effect`, `overlay`, `text-treatment` + +--- + +## Component Template + +```html + + + + + + + + +
+ +
+ + + +``` + +**Replace checklist:** + +- `COMPNAME` → your component name (e.g., `shimmer-sweep`) +- Background should be `transparent` so it overlays cleanly +- No `data-composition-id` or `window.__timelines` — the parent owns timing diff --git a/.agents/skills/css-animations/SKILL.md b/.agents/skills/css-animations/SKILL.md new file mode 100644 index 0000000..005a9ba --- /dev/null +++ b/.agents/skills/css-animations/SKILL.md @@ -0,0 +1,124 @@ +--- +name: css-animations +description: CSS animation adapter patterns for HyperFrames. Use when authoring CSS keyframes, animation-delay based timing, animation-fill-mode, animation-play-state, or CSS-only motion that HyperFrames must seek deterministically during preview and rendering. +--- + +# CSS Animations for HyperFrames + +HyperFrames can seek CSS keyframe animations through its `css` runtime adapter. Use this for simple repeated motifs, background motion, shimmer, glow, masks, and non-sequenced decoration. + +For scene choreography, GSAP is usually clearer. CSS animations work best when the motion belongs to one element and has a fixed duration. + +## Contract + +- Put the animated element in the DOM before runtime initialization finishes. +- Give timed elements a `data-start` value so local animation time matches the clip. +- Use finite `animation-duration` and `animation-iteration-count` because the negative-delay fallback cannot represent unbounded duration in environments without WAAPI-backed CSS animations. +- Prefer `animation-fill-mode: both` so seeked states hold before and after active motion. +- Avoid wall-clock JavaScript, hover-triggered state, and class toggles that depend on user events. + +The adapter discovers elements with computed `animation-name`, seeks their browser `Animation` handles when available, and falls back to pausing with negative `animation-delay`. + +## Basic Pattern + +```html +
+ + +``` + +## Stagger Pattern + +Use CSS custom properties to avoid duplicating keyframes: + +```html +
+ + + +
+ + +``` + +## Good Uses + +- Decorative loops with a known repeat count. +- Mask, glow, shimmer, grain, and subtle parallax layers. +- Simple one-element entrances where a full JS timeline would be excessive. + +## Avoid + +- Infinite CSS animations unless you have verified the browser exposes seekable WAAPI-backed CSS animation handles. Prefer a finite iteration count covering the visible duration. +- Animating layout properties like `top`, `left`, `width`, or `height` when transforms work. +- Relying on hover, focus, scroll, or media queries to trigger render-critical motion. +- Changing animation classes after startup unless another deterministic timeline controls that change. + +## Validation + +After editing CSS animation compositions: + +```bash +npx hyperframes lint +npx hyperframes validate +``` + +## Credits And References + +- HyperFrames adapter source: `packages/core/src/runtime/adapters/css.ts`. +- MDN CSS animation documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/animation +- MDN `animation-fill-mode`: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode diff --git a/.agents/skills/gsap/SKILL.md b/.agents/skills/gsap/SKILL.md new file mode 100644 index 0000000..db40a6c --- /dev/null +++ b/.agents/skills/gsap/SKILL.md @@ -0,0 +1,240 @@ +--- +name: gsap +description: GSAP animation reference for HyperFrames. Covers gsap.to(), from(), fromTo(), easing, stagger, defaults, timelines (gsap.timeline(), position parameter, labels, nesting, playback), and performance (transforms, will-change, quickTo). Use when writing GSAP animations in HyperFrames compositions. +--- + +# GSAP + +## HyperFrames Contract + +HyperFrames controls GSAP through its `gsap` runtime adapter. Create a paused timeline synchronously, register it on `window.__timelines` with the exact `data-composition-id`, and let HyperFrames seek it. + +```html + + +``` + +- The registry key must match the composition root's `data-composition-id`. +- Do not call `tl.play()` for render-critical motion. +- Do not build timelines inside async code, timers, or event handlers. +- Keep loops finite. HyperFrames renders finite video durations. + +## Core Tween Methods + +- **gsap.to(targets, vars)** — animate from current state to `vars`. Most common. +- **gsap.from(targets, vars)** — animate from `vars` to current state (entrances). +- **gsap.fromTo(targets, fromVars, toVars)** — explicit start and end. +- **gsap.set(targets, vars)** — apply immediately (duration 0). + +Always use **camelCase** property names (e.g. `backgroundColor`, `rotationX`). + +## Common vars + +- **duration** — seconds (default 0.5). +- **delay** — seconds before start. +- **ease** — `"power1.out"` (default), `"power3.inOut"`, `"back.out(1.7)"`, `"elastic.out(1, 0.3)"`, `"none"`. +- **stagger** — number `0.1` or object: `{ amount: 0.3, from: "center" }`, `{ each: 0.1, from: "random" }`. +- **overwrite** — `false` (default), `true`, or `"auto"`. +- **repeat** — finite number; never `-1` in HyperFrames. Compute repeats from the visible duration. **yoyo** — alternates direction with repeat. +- **onComplete**, **onStart**, **onUpdate** — callbacks. +- **immediateRender** — default `true` for from()/fromTo(). Set `false` on later tweens targeting the same property+element to avoid overwrite. + +## Transforms and CSS + +Prefer GSAP's **transform aliases** over raw `transform` string: + +| GSAP property | Equivalent | +| --------------------------- | ------------------- | +| `x`, `y`, `z` | translateX/Y/Z (px) | +| `xPercent`, `yPercent` | translateX/Y in % | +| `scale`, `scaleX`, `scaleY` | scale | +| `rotation` | rotate (deg) | +| `rotationX`, `rotationY` | 3D rotate | +| `skewX`, `skewY` | skew | +| `transformOrigin` | transform-origin | + +- **autoAlpha** — prefer over `opacity`. At 0: also sets `visibility: hidden`. +- **CSS variables** — `"--hue": 180`. +- **svgOrigin** _(SVG only)_ — global SVG coordinate space origin. Don't combine with `transformOrigin`. +- **Directional rotation** — `"360_cw"`, `"-170_short"`, `"90_ccw"`. +- **clearProps** — `"all"` or comma-separated; removes inline styles on complete. +- **Relative values** — `"+=20"`, `"-=10"`, `"*=2"`. + +## Function-Based Values + +```javascript +gsap.to(".item", { + x: (i, target, targets) => i * 50, + stagger: 0.1, +}); +``` + +## Easing + +Built-in eases: `power1`–`power4`, `back`, `bounce`, `circ`, `elastic`, `expo`, `sine`. Each has `.in`, `.out`, `.inOut`. + +## Defaults + +```javascript +gsap.defaults({ duration: 0.6, ease: "power2.out" }); +``` + +## Controlling Tweens + +```javascript +const tween = gsap.to(".box", { x: 100 }); +tween.pause(); +tween.play(); +tween.reverse(); +tween.kill(); +tween.progress(0.5); +tween.time(0.2); +``` + +## gsap.matchMedia() (Responsive + Accessibility) + +Runs setup only when a media query matches; auto-reverts when it stops matching. + +```javascript +let mm = gsap.matchMedia(); +mm.add( + { + isDesktop: "(min-width: 800px)", + reduceMotion: "(prefers-reduced-motion: reduce)", + }, + (context) => { + const { isDesktop, reduceMotion } = context.conditions; + gsap.to(".box", { + rotation: isDesktop ? 360 : 180, + duration: reduceMotion ? 0 : 2, + }); + }, +); +``` + +--- + +## Timelines + +### Creating a Timeline + +```javascript +const tl = gsap.timeline({ defaults: { duration: 0.5, ease: "power2.out" } }); +tl.to(".a", { x: 100 }).to(".b", { y: 50 }).to(".c", { opacity: 0 }); +``` + +### Position Parameter + +Third argument controls placement: + +- **Absolute**: `1` — at 1s +- **Relative**: `"+=0.5"` — after end; `"-=0.2"` — before end +- **Label**: `"intro"`, `"intro+=0.3"` +- **Alignment**: `"<"` — same start as previous; `">"` — after previous ends; `"<0.2"` — 0.2s after previous starts + +```javascript +tl.to(".a", { x: 100 }, 0); +tl.to(".b", { y: 50 }, "<"); // same start as .a +tl.to(".c", { opacity: 0 }, "<0.2"); // 0.2s after .b starts +``` + +### Labels + +```javascript +tl.addLabel("intro", 0); +tl.to(".a", { x: 100 }, "intro"); +tl.addLabel("outro", "+=0.5"); +tl.play("outro"); +tl.tweenFromTo("intro", "outro"); +``` + +### Timeline Options + +- **paused: true** — create paused; call `.play()` to start. +- **repeat**, **yoyo** — apply to whole timeline. +- **defaults** — vars merged into every child tween. + +### Nesting Timelines + +```javascript +const master = gsap.timeline(); +const child = gsap.timeline(); +child.to(".a", { x: 100 }).to(".b", { y: 50 }); +master.add(child, 0); +``` + +### Playback Control + +`tl.play()`, `tl.pause()`, `tl.reverse()`, `tl.restart()`, `tl.time(2)`, `tl.progress(0.5)`, `tl.kill()`. + +--- + +## Performance + +### Prefer Transform and Opacity + +Animating `x`, `y`, `scale`, `rotation`, `opacity` stays on the compositor. Avoid `width`, `height`, `top`, `left` when transforms achieve the same effect. + +### will-change + +```css +will-change: transform; +``` + +Only on elements that actually animate. + +### gsap.quickTo() for Frequent Updates + +```javascript +let xTo = gsap.quickTo("#id", "x", { duration: 0.4, ease: "power3" }), + yTo = gsap.quickTo("#id", "y", { duration: 0.4, ease: "power3" }); +container.addEventListener("mousemove", (e) => { + xTo(e.pageX); + yTo(e.pageY); +}); +``` + +### Stagger > Many Tweens + +Use `stagger` instead of separate tweens with manual delays. + +### Cleanup + +Pause or kill off-screen animations. + +--- + +## References (loaded on demand) + +- **[references/effects.md](references/effects.md)** — Drop-in effects: typewriter text, audio visualizer. Read when needing ready-made effect patterns for HyperFrames. + +## Best Practices + +- Use camelCase property names; prefer transform aliases and autoAlpha. +- Prefer timelines over chaining with delay; use the position parameter. +- Add labels with `addLabel()` for readable sequencing. +- Pass defaults into timeline constructor. +- Store tween/timeline return value when controlling playback. + +## Do Not + +- Animate layout properties (width/height/top/left) when transforms suffice. +- Use both svgOrigin and transformOrigin on the same SVG element. +- Chain animations with delay when a timeline can sequence them. +- Create tweens before the DOM exists. +- Skip cleanup — always kill tweens when no longer needed. +- Use infinite repeat values in HyperFrames compositions. Use finite repeat counts computed from the visible duration. + +## Credits And References + +- HyperFrames adapter source: `packages/core/src/runtime/adapters/gsap.ts`. +- GSAP documentation: https://gsap.com/docs/v3/ +- GSAP timeline pause and seek behavior: https://gsap.com/docs/v3/GSAP/Timeline/pause%28%29/ diff --git a/.agents/skills/gsap/references/effects.md b/.agents/skills/gsap/references/effects.md new file mode 100644 index 0000000..82c0eba --- /dev/null +++ b/.agents/skills/gsap/references/effects.md @@ -0,0 +1,297 @@ +# GSAP Effects for HyperFrames + +Drop-in animation patterns for HyperFrames compositions. Each effect is self-contained with HTML, CSS, and code. + +All effects follow HyperFrames composition rules — deterministic, no randomness, timelines registered via `window.__timelines`. + +## Table of Contents + +- [Typewriter](#typewriter) +- [Audio Visualizer](#audio-visualizer) + +--- + +## Typewriter + +Reveal text character by character using GSAP's TextPlugin. + +### Required Plugin + +```html + + + +``` + +### Basic Typewriter + +```js +const text = "Hello, world!"; +const cps = 10; // chars per second: 3-5 dramatic, 8-12 conversational, 15-20 energetic +tl.to( + "#typed-text", + { text: { value: text }, duration: text.length / cps, ease: "none" }, + startTime, +); +``` + +### With Blinking Cursor + +Three rules: + +1. **One cursor visible at a time** — hide previous before showing next. +2. **Cursor must blink when idle** — after typing, during pauses. +3. **No gap between text and cursor** — elements must be flush in HTML. + +```html +| +``` + +```css +@keyframes blink { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0; + } +} +.cursor-blink { + animation: blink 0.8s step-end infinite; +} +.cursor-solid { + animation: none; + opacity: 1; +} +.cursor-hide { + animation: none; + opacity: 0; +} +``` + +Pattern: blink → solid (typing starts) → type → solid → blink (typing done). + +```js +tl.call(() => cursor.classList.replace("cursor-blink", "cursor-solid"), [], startTime); +tl.to("#typed-text", { text: { value: text }, duration: dur, ease: "none" }, startTime); +tl.call(() => cursor.classList.replace("cursor-solid", "cursor-blink"), [], startTime + dur); +``` + +### Backspacing + +TextPlugin removes from front — wrong for backspace. Use manual substring removal: + +```js +function backspace(tl, selector, word, startTime, cps) { + const el = document.querySelector(selector); + const interval = 1 / cps; + for (let i = word.length - 1; i >= 0; i--) { + tl.call( + () => { + el.textContent = word.slice(0, i); + }, + [], + startTime + (word.length - i) * interval, + ); + } + return word.length * interval; +} +``` + +### Spacing with Static Text + +When a typewriter word sits next to static text, use `margin-left` on a wrapper span. Don't use flex gap (spaces cursor from text) or trailing space in static text (collapses when dynamic is empty). + +```html +
+ Ship something + | +
+``` + +### Word Rotation + +Type → hold → backspace → next word. Cursor blinks during every idle moment (holds, after backspace). + +```js +words.forEach((word, i) => { + const typeDur = word.length / 10; + // Solid while typing + tl.call(() => cursor.classList.replace("cursor-blink", "cursor-solid"), [], offset); + tl.to("#typed-text", { text: { value: word }, duration: typeDur, ease: "none" }, offset); + // Blink during hold + tl.call(() => cursor.classList.replace("cursor-solid", "cursor-blink"), [], offset + typeDur); + offset += typeDur + 1.5; // hold + + if (i < words.length - 1) { + tl.call(() => cursor.classList.replace("cursor-blink", "cursor-solid"), [], offset); + const clearDur = backspace(tl, el, word, offset, 20); + tl.call(() => cursor.classList.replace("cursor-solid", "cursor-blink"), [], offset + clearDur); + offset += clearDur + 0.3; + } +}); +``` + +### Appending Words + +Build a sentence word-by-word into the same element: + +```js +let accumulated = ""; +words.forEach((word) => { + const target = accumulated + (accumulated ? " " : "") + word; + const newChars = target.length - accumulated.length; + tl.to("#typed-text", { text: { value: target }, duration: newChars / 10, ease: "none" }, offset); + accumulated = target; + offset += newChars / 10 + 0.3; +}); +``` + +### Multi-Line Cursor Handoff + +When handing off between typewriter lines: hide previous → blink new → pause → solid when typing. Never go hidden→solid (skips idle state). + +```js +tl.call( + () => { + prevCursor.classList.replace("cursor-blink", "cursor-hide"); + nextCursor.classList.replace("cursor-hide", "cursor-blink"); + }, + [], + handoffTime, +); + +const typeStart = handoffTime + 0.5; // brief blink pause +tl.call(() => nextCursor.classList.replace("cursor-blink", "cursor-solid"), [], typeStart); +tl.to("#next-text", { text: { value: text }, duration: dur, ease: "none" }, typeStart); +tl.call(() => nextCursor.classList.replace("cursor-solid", "cursor-blink"), [], typeStart + dur); +``` + +### Timing Guide + +| CPS | Feel | Good for | +| ----- | ---------------- | -------------------------- | +| 3-5 | Slow, deliberate | Dramatic reveals, suspense | +| 8-12 | Natural typing | Dialogue, narration | +| 15-20 | Fast, energetic | Tech demos, code | +| 30+ | Near-instant | Filling long blocks | + +--- + +## Audio Visualizer + +Pre-extract audio data, drive canvas/DOM rendering from GSAP timeline. + +### Extract Audio Data + +```bash +python scripts/extract-audio-data.py audio.mp3 -o audio-data.json +python scripts/extract-audio-data.py video.mp4 --fps 30 --bands 16 -o audio-data.json +``` + +Requires ffmpeg and numpy. + +### Data Format + +```json +{ + "fps": 30, "totalFrames": 5415, + "frames": [{ "time": 0.0, "rms": 0.42, "bands": [0.8, 0.6, 0.3, ...] }] +} +``` + +- **rms** (0-1): overall loudness, normalized across track +- **bands[]** (0-1): frequency magnitudes. Index 0 = bass, higher = treble. Each normalized independently. + +### Loading the Data + +```js +// Option A: inline (small files, under ~500KB) +var AUDIO_DATA = { + /* paste audio-data.json contents */ +}; + +// Option B: sync XHR (large files — must be synchronous for deterministic timeline construction) +var xhr = new XMLHttpRequest(); +xhr.open("GET", "audio-data.json", false); +xhr.send(); +var AUDIO_DATA = JSON.parse(xhr.responseText); +``` + +**Do NOT use async `fetch()` to load audio data.** HyperFrames requires synchronous timeline construction — the capture engine reads `window.__timelines` synchronously after page load. Building timelines inside `.then()` callbacks means the timeline isn't ready when capture starts. + +### Rendering Approaches + +**Canvas 2D** (most common — bars, waveforms, circles, gradients): + +```js +for (let f = 0; f < AUDIO_DATA.totalFrames; f++) { + tl.call( + () => { + const frame = AUDIO_DATA.frames[f]; + ctx.clearRect(0, 0, canvas.width, canvas.height); + // draw using frame.rms and frame.bands + }, + [], + f / AUDIO_DATA.fps, + ); +} +``` + +**WebGL / Three.js** — HyperFrames patches `THREE.Clock` for deterministic time. Update uniforms from audio data each frame. + +**DOM Elements** — fine for < 20 elements, less performant than Canvas for many. + +### Spatial Mapping + +- **Horizontal**: bass left, treble right (iterate bands left-to-right) +- **Vertical**: bass bottom, treble top +- **Circular**: bass at 12 o'clock, wrap clockwise; mirror for full circle + +### Smoothing + +```js +let prev = null; +const smoothing = 0.25; // 0.1-0.2 snappy, 0.3-0.5 flowing +function smooth(f) { + const raw = AUDIO_DATA.frames[f]; + if (!prev) { + prev = { rms: raw.rms, bands: [...raw.bands] }; + return prev; + } + prev = { + rms: prev.rms * smoothing + raw.rms * (1 - smoothing), + bands: raw.bands.map((b, i) => prev.bands[i] * smoothing + b * (1 - smoothing)), + }; + return prev; +} +``` + +### Motion Principles + +- **Bass drives big moves** — scale, glow, position shifts +- **Treble drives detail** — shimmer, flicker, edge effects +- **RMS drives globals** — background brightness, overall energy +- Pick 2-3 properties to animate. More looks noisy. +- Keep minimums above zero — quiet sections need life. + +### Band Count + +| Bands | Detail | Good for | +| ----- | --------- | -------------------------- | +| 4 | Low | Background glow, pulsing | +| 8 | Medium | Bar charts, basic spectrum | +| 16 | High | Detailed EQ (default) | +| 32 | Very high | Dense radial layouts | + +### Layering + +Layer multiple canvases with CSS z-index for depth — a background layer driven by bass/rms and a foreground layer driven by individual bands creates depth without complexity. + +```html + + +``` diff --git a/.agents/skills/gsap/scripts/extract-audio-data.py b/.agents/skills/gsap/scripts/extract-audio-data.py new file mode 100644 index 0000000..b4efba7 --- /dev/null +++ b/.agents/skills/gsap/scripts/extract-audio-data.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +""" +Extract per-frame audio visualization data from an audio or video file. + +Outputs JSON with RMS amplitude and frequency band data at the target FPS, +ready to embed in a HyperFrames composition. + +Usage: + python extract-audio-data.py input.mp3 -o audio-data.json + python extract-audio-data.py input.mp4 --fps 30 --bands 16 -o audio-data.json + +Requirements: + - Python 3.9+ + - ffmpeg (for decoding audio) + - numpy (pip install numpy) +""" + +import argparse +import json +import subprocess +import sys + +import numpy as np + +# --------------------------------------------------------------------------- +# FFT parameters +# +# A 4096-sample window gives ~10.8 Hz per bin at 44100Hz — enough to resolve +# low-frequency bands cleanly. The per-frame audio slice (44100/30 = 1470 +# samples at 30fps) is too small and causes low bands to map to the same bins. +# +# Frequency range 30Hz–16kHz covers the useful range for music. Below 30Hz is +# sub-bass most speakers can't reproduce; above 16kHz is noise/harmonics that +# don't contribute to perceived rhythm or melody. +# --------------------------------------------------------------------------- + +SAMPLE_RATE = 44100 +FFT_SIZE = 4096 +MIN_FREQ = 30.0 +MAX_FREQ = 16000.0 + + +def decode_audio(path: str) -> np.ndarray: + """Decode audio to mono float32 samples via ffmpeg.""" + cmd = [ + "ffmpeg", "-i", path, + "-vn", "-ac", "1", "-ar", str(SAMPLE_RATE), + "-f", "s16le", "-acodec", "pcm_s16le", + "-loglevel", "error", + "pipe:1", + ] + result = subprocess.run(cmd, capture_output=True) + if result.returncode != 0: + print(f"ffmpeg error: {result.stderr.decode()}", file=sys.stderr) + sys.exit(1) + return np.frombuffer(result.stdout, dtype=np.int16).astype(np.float32) / 32768.0 + + +def compute_band_edges(n_bands: int) -> np.ndarray: + """Logarithmically-spaced frequency band edges from MIN_FREQ to MAX_FREQ.""" + return np.array([ + MIN_FREQ * (MAX_FREQ / MIN_FREQ) ** (i / n_bands) + for i in range(n_bands + 1) + ]) + + +def compute_fft_bands( + windowed: np.ndarray, freq_per_bin: float, n_bins: int, + band_edges: np.ndarray, n_bands: int, +) -> np.ndarray: + """Compute peak magnitude in logarithmically-spaced frequency bands.""" + magnitudes = np.abs(np.fft.rfft(windowed)) + + bands = np.zeros(n_bands) + for b in range(n_bands): + low_bin = max(0, int(band_edges[b] / freq_per_bin)) + high_bin = min(n_bins, int(band_edges[b + 1] / freq_per_bin)) + if high_bin <= low_bin: + high_bin = low_bin + 1 + # Clamp to valid range to avoid empty slices + low_bin = min(low_bin, n_bins - 1) + high_bin = min(high_bin, n_bins) + bands[b] = np.max(magnitudes[low_bin:high_bin]) + + return bands + + +def extract(path: str, fps: int, n_bands: int) -> dict: + """Extract per-frame audio data.""" + print(f"Decoding audio from {path}...", file=sys.stderr) + samples = decode_audio(path) + duration = len(samples) / SAMPLE_RATE + frame_step = SAMPLE_RATE // fps + total_frames = int(duration * fps) + + print(f"Duration: {duration:.1f}s, {total_frames} frames at {fps}fps", file=sys.stderr) + print(f"FFT window: {FFT_SIZE} samples ({SAMPLE_RATE / FFT_SIZE:.1f} Hz/bin)", file=sys.stderr) + print(f"Frequency range: {MIN_FREQ:.0f}-{MAX_FREQ:.0f} Hz, {n_bands} bands", file=sys.stderr) + + # Precompute constants + hann = np.hanning(FFT_SIZE) + band_edges = compute_band_edges(n_bands) + freq_per_bin = SAMPLE_RATE / FFT_SIZE + n_bins = FFT_SIZE // 2 + 1 + half_fft = FFT_SIZE // 2 + + # Pass 1: extract raw values + rms_values = np.zeros(total_frames) + band_values = np.zeros((total_frames, n_bands)) + + for f in range(total_frames): + # RMS from the frame's audio slice + rms_start = f * frame_step + rms_end = rms_start + frame_step + frame_slice = samples[rms_start:min(rms_end, len(samples))] + if len(frame_slice) > 0: + rms_values[f] = np.sqrt(np.mean(frame_slice ** 2)) + + # FFT from a centered 4096-sample window + center = rms_start + frame_step // 2 + win_start = center - half_fft + win_end = center + half_fft + + if win_start >= 0 and win_end <= len(samples): + window = samples[win_start:win_end] * hann + else: + # Zero-pad at edges + padded = np.zeros(FFT_SIZE) + src_start = max(0, win_start) + src_end = min(len(samples), win_end) + dst_start = src_start - win_start + dst_end = dst_start + (src_end - src_start) + padded[dst_start:dst_end] = samples[src_start:src_end] + window = padded * hann + + band_values[f] = compute_fft_bands(window, freq_per_bin, n_bins, band_edges, n_bands) + + # Pass 2: normalize + peak_rms = rms_values.max() if total_frames > 0 else 1.0 + if peak_rms > 0: + rms_values /= peak_rms + + # Per-band normalization so treble is visible alongside louder bass + band_peaks = band_values.max(axis=0) + band_peaks[band_peaks == 0] = 1.0 + band_values /= band_peaks + + # Build output + frames = [] + for f in range(total_frames): + frames.append({ + "time": round(f / fps, 4), + "rms": round(float(rms_values[f]), 4), + "bands": [round(float(b), 4) for b in band_values[f]], + }) + + return { + "duration": round(duration, 4), + "fps": fps, + "bands": n_bands, + "totalFrames": total_frames, + "frames": frames, + } + + +def main(): + parser = argparse.ArgumentParser(description="Extract per-frame audio visualization data") + parser.add_argument("input", help="Audio or video file") + parser.add_argument("-o", "--output", default="audio-data.json", help="Output JSON path") + parser.add_argument("--fps", type=int, default=30, help="Frames per second (default: 30)") + parser.add_argument("--bands", type=int, default=16, help="Number of frequency bands (default: 16)") + args = parser.parse_args() + + if args.fps < 1: + parser.error("--fps must be at least 1") + if args.bands < 1: + parser.error("--bands must be at least 1") + + data = extract(args.input, args.fps, args.bands) + + with open(args.output, "w") as f: + json.dump(data, f) + + print(f"Wrote {args.output} ({data['totalFrames']} frames, {data['bands']} bands)", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/.agents/skills/hyperframes-cli/SKILL.md b/.agents/skills/hyperframes-cli/SKILL.md new file mode 100644 index 0000000..9b080c5 --- /dev/null +++ b/.agents/skills/hyperframes-cli/SKILL.md @@ -0,0 +1,144 @@ +--- +name: hyperframes-cli +description: HyperFrames CLI dev loop — `npx hyperframes` for scaffolding (init), validation (lint, inspect), preview, render, and environment troubleshooting (doctor, browser, info, upgrade). Use when running any of these commands or troubleshooting the HyperFrames build/render environment. For asset preprocessing commands (`tts`, `transcribe`, `remove-background`), invoke the `hyperframes-media` skill instead. +--- + +# HyperFrames CLI + +Everything runs through `npx hyperframes`. Requires Node.js >= 22 and FFmpeg. + +## Workflow + +1. **Scaffold** — `npx hyperframes init my-video` +2. **Write** — author HTML composition (see the `hyperframes` skill) +3. **Lint** — `npx hyperframes lint` +4. **Visual inspect** — `npx hyperframes inspect` +5. **Preview** — `npx hyperframes preview` +6. **Render** — `npx hyperframes render` + +Lint and inspect before preview. `lint` catches missing `data-composition-id`, overlapping tracks, and unregistered timelines. `inspect` opens the rendered composition in headless Chrome, seeks through the timeline, and reports text spilling out of bubbles/containers or off the canvas. + +## Scaffolding + +```bash +npx hyperframes init my-video # interactive wizard +npx hyperframes init my-video --example warm-grain # pick an example +npx hyperframes init my-video --video clip.mp4 # with video file +npx hyperframes init my-video --audio track.mp3 # with audio file +npx hyperframes init my-video --example blank --tailwind # with Tailwind v4 browser runtime +npx hyperframes init my-video --non-interactive # skip prompts (CI/agents) +``` + +Templates: `blank`, `warm-grain`, `play-mode`, `swiss-grid`, `vignelli`, `decision-tree`, `kinetic-type`, `product-promo`, `nyt-graph`. + +`init` creates the right file structure, copies media, transcribes audio with Whisper, and installs AI coding skills. Use it instead of creating files by hand. + +When using `--tailwind`, invoke the `tailwind` skill before editing classes or theme tokens. The scaffold uses Tailwind v4.2 via the browser runtime, not Studio's Tailwind v3 setup. + +## Linting + +```bash +npx hyperframes lint # current directory +npx hyperframes lint ./my-project # specific project +npx hyperframes lint --verbose # info-level findings +npx hyperframes lint --json # machine-readable +``` + +Lints `index.html` and all files in `compositions/`. Reports errors (must fix), warnings (should fix), and info (with `--verbose`). + +## Visual Inspect + +```bash +npx hyperframes inspect # inspect rendered layout over the timeline +npx hyperframes inspect ./my-project # specific project +npx hyperframes inspect --json # agent-readable findings +npx hyperframes inspect --samples 15 # denser timeline sweep +npx hyperframes inspect --at 1.5,4,7.25 # explicit hero-frame timestamps +``` + +Use this after `lint` and `validate`, especially for compositions with speech bubbles, cards, captions, or tight typography. It reports: + +- Text extending outside the nearest visual container or bubble +- Text clipped by its own fixed-width/fixed-height box +- Text extending outside the composition canvas +- Children escaping clipping containers + +Errors should be fixed before rendering. Warnings are surfaced for agent review; add `--strict` to fail on warnings too. Repeated static issues are collapsed by default so JSON output stays compact for LLM context windows. If overflow is intentional for an entrance/exit animation, mark the element or ancestor with `data-layout-allow-overflow`. If a decorative element should never be audited, mark it with `data-layout-ignore`. + +`npx hyperframes layout` remains available as a compatibility alias for the same visual inspection pass. + +## Previewing + +```bash +npx hyperframes preview # serve current directory +npx hyperframes preview --port 4567 # custom port (default 3002) +``` + +Hot-reloads on file changes. Opens the studio in your browser automatically. + +When handing a project back to the user, use the Studio project URL, not the +source `index.html` path: + +```text +http://localhost:/#project/ +``` + +Use the actual port from the preview output and the project directory name. For +example, after `npx hyperframes preview --port 3017` in `codex-openai-video`, +report `http://localhost:3017/#project/codex-openai-video`. + +Treat `index.html` as source-code context only. It is fine to link it as an +implementation file, but do not label it as the project or preview surface. + +## Rendering + +```bash +npx hyperframes render # standard MP4 +npx hyperframes render --output final.mp4 # named output +npx hyperframes render --quality draft # fast iteration +npx hyperframes render --fps 60 --quality high # final delivery +npx hyperframes render --format webm # transparent WebM +npx hyperframes render --docker # byte-identical +``` + +| Flag | Options | Default | Notes | +| -------------------- | --------------------- | -------------------------- | ------------------------------------------------------------------ | +| `--output` | path | renders/name_timestamp.mp4 | Output path | +| `--fps` | 24, 30, 60 | 30 | 60fps doubles render time | +| `--quality` | draft, standard, high | standard | draft for iterating | +| `--format` | mp4, webm | mp4 | WebM supports transparency | +| `--workers` | 1-8 or auto | auto | Each spawns Chrome | +| `--docker` | flag | off | Reproducible output | +| `--gpu` | flag | off | GPU-accelerated encoding | +| `--strict` | flag | off | Fail on lint errors | +| `--strict-all` | flag | off | Fail on errors AND warnings | +| `--variables` | JSON object | — | Override variable values declared in `data-composition-variables` | +| `--variables-file` | path | — | JSON file with variable values (alternative to `--variables`) | +| `--strict-variables` | flag | off | Fail render on undeclared keys or type mismatches in `--variables` | + +**Quality guidance:** `draft` while iterating, `standard` for review, `high` for final delivery. + +**Parametrized renders:** the composition declares its variables on the `` root with **`data-composition-variables`** — a JSON **array of declarations** (`{id, type, label, default}` per entry) that defines the schema. Scripts inside read the resolved values via `window.__hyperframes.getVariables()`. The CLI **`--variables '{"title":"Q4 Report"}'`** is a JSON **object keyed by id** that overrides those declared defaults for one render; missing keys fall through, so the same composition runs unchanged in dev preview and in production. (Sub-comp hosts can also override per-instance with **`data-variable-values`** — same object shape, scoped to one mount of the sub-composition. See the `hyperframes` skill for the full pattern.) + +## Asset Preprocessing + +`npx hyperframes tts`, `transcribe`, and `remove-background` produce assets (narration audio, word-level transcripts, transparent video) that get dropped into a composition. Each downloads its own model on first run. For voice selection, whisper model rules (the `.en`-translates-non-English gotcha), output format choice (VP9 alpha WebM vs ProRes), and the TTS → transcribe → captions chain, invoke the `hyperframes-media` skill. + +## Troubleshooting + +```bash +npx hyperframes doctor # check environment (Chrome, FFmpeg, Node, memory) +npx hyperframes browser # manage bundled Chrome +npx hyperframes info # version and environment details +npx hyperframes upgrade # check for updates +``` + +Run `doctor` first if rendering fails. Common issues: missing FFmpeg, missing Chrome, low memory. + +## Other + +```bash +npx hyperframes compositions # list compositions in project +npx hyperframes docs # open documentation +npx hyperframes benchmark . # benchmark render performance +``` diff --git a/.agents/skills/hyperframes-media/SKILL.md b/.agents/skills/hyperframes-media/SKILL.md new file mode 100644 index 0000000..13e3d4a --- /dev/null +++ b/.agents/skills/hyperframes-media/SKILL.md @@ -0,0 +1,243 @@ +--- +name: hyperframes-media +description: Asset preprocessing for HyperFrames compositions — text-to-speech narration (Kokoro), audio/video transcription (Whisper), and background removal for transparent overlays (u2net). Use when generating voiceover from text, transcribing speech for captions, removing the background from a video or image to use as a transparent overlay, choosing a TTS voice or whisper model, or chaining these (TTS → transcribe → captions). Each command downloads its own model on first run. +--- + +# HyperFrames Media Preprocessing + +Three CLI commands that produce assets for compositions: `tts` (speech), `transcribe` (timestamps), and `remove-background` (transparent video). Each downloads a model on first run and caches it under `~/.cache/hyperframes/`. Drop the output into the project, then reference it from the composition HTML — see the `hyperframes` skill for the audio/video element conventions. + +## Text-to-Speech (`tts`) + +Generate speech audio locally with Kokoro-82M. No API key. + +```bash +npx hyperframes tts "Text here" --voice af_nova --output narration.wav +npx hyperframes tts script.txt --voice bf_emma --output narration.wav +npx hyperframes tts --list # all 54 voices +``` + +### Voice Selection + +Match voice to content. Default is `af_heart`. + +| Content type | Voice | Why | +| ----------------- | --------------------- | ----------------------------- | +| Product demo | `af_heart`/`af_nova` | Warm, professional | +| Tutorial / how-to | `am_adam`/`bf_emma` | Neutral, easy to follow | +| Marketing / promo | `af_sky`/`am_michael` | Energetic or authoritative | +| Documentation | `bf_emma`/`bm_george` | Clear British English, formal | +| Casual / social | `af_heart`/`af_sky` | Approachable, natural | + +### Multilingual + +Voice IDs encode language in the first letter: `a`=American English, `b`=British English, `e`=Spanish, `f`=French, `h`=Hindi, `i`=Italian, `j`=Japanese, `p`=Brazilian Portuguese, `z`=Mandarin. The CLI auto-detects the phonemizer locale from the prefix — no `--lang` needed when the voice matches the text. + +```bash +npx hyperframes tts "La reunión empieza a las nueve" --voice ef_dora --output es.wav +npx hyperframes tts "今日はいい天気ですね" --voice jf_alpha --output ja.wav +``` + +Use `--lang` only to override auto-detection (stylized accents). Valid codes: `en-us`, `en-gb`, `es`, `fr-fr`, `hi`, `it`, `pt-br`, `ja`, `zh`. Non-English phonemization requires `espeak-ng` system-wide (`brew install espeak-ng` / `apt-get install espeak-ng`). + +### Speed + +- `0.7-0.8` — tutorial, complex content, accessibility +- `1.0` — natural pace (default) +- `1.1-1.2` — intros, transitions, upbeat content +- `1.5+` — rarely appropriate; test carefully + +### Long Scripts + +For more than a few paragraphs, write to a `.txt` file and pass the path. Inputs over ~5 minutes of speech may benefit from splitting into segments. + +### Requirements + +Python 3.8+ with `kokoro-onnx` and `soundfile` (`pip install kokoro-onnx soundfile`). Model downloads on first use (~311 MB + ~27 MB voices, cached in `~/.cache/hyperframes/tts/`). + +## Transcription (`transcribe`) + +Produce a normalized `transcript.json` with word-level timestamps. + +```bash +npx hyperframes transcribe audio.mp3 +npx hyperframes transcribe video.mp4 --model small --language es +npx hyperframes transcribe subtitles.srt # import existing +npx hyperframes transcribe subtitles.vtt +npx hyperframes transcribe openai-response.json +``` + +### Language Rule (Non-Negotiable) + +**Never use `.en` models unless the user explicitly states the audio is English.** `.en` models (`small.en`, `medium.en`) **translate** non-English audio into English instead of transcribing it. This silently destroys the original language. + +1. Language known and non-English → `--model small --language ` (no `.en` suffix) +2. Language known and English → `--model small.en` +3. Language unknown → `--model small` (no `.en`, no `--language`) — whisper auto-detects + +**Default model is `small`, not `small.en`.** + +### Model Sizes + +| Model | Size | Speed | When to use | +| ---------- | ------ | -------- | ------------------------------------- | +| `tiny` | 75 MB | Fastest | Quick previews, testing pipeline | +| `base` | 142 MB | Fast | Short clips, clear audio | +| `small` | 466 MB | Moderate | **Default** — most content | +| `medium` | 1.5 GB | Slow | Important content, noisy audio, music | +| `large-v3` | 3.1 GB | Slowest | Production quality | + +Music with vocals: start at `medium` minimum; produced tracks often need manual SRT/VTT import. For caption-quality checks (mandatory after every transcription), the cleaning JS, retry rules, and the OpenAI/Groq API import path, see [hyperframes/references/transcript-guide.md](../hyperframes/references/transcript-guide.md). + +### Output Shape + +Compositions consume a flat array of word objects. The `id` field (`w0`, `w1`, ...) is added during normalization for stable references in caption overrides; it's optional for backwards compatibility. + +```json +[ + { "id": "w0", "text": "Hello", "start": 0.0, "end": 0.5 }, + { "id": "w1", "text": "world.", "start": 0.6, "end": 1.2 } +] +``` + +## Background Removal (`remove-background`) + +Remove the background from a video or image so the subject (typically a person — avatar, presenter, talking head) sits as a transparent overlay in a composition. + +```bash +npx hyperframes remove-background subject.mp4 -o transparent.webm # default: VP9 alpha WebM +npx hyperframes remove-background subject.mp4 -o transparent.mov # ProRes 4444 (editing) +npx hyperframes remove-background portrait.jpg -o cutout.png # single-image cutout +npx hyperframes remove-background subject.mp4 -o subject.webm \ + --background-output plate.webm # both layers in one pass +npx hyperframes remove-background subject.mp4 -o transparent.webm --device cpu +npx hyperframes remove-background --info # detected providers +``` + +Uses `u2net_human_seg` (MIT). First run downloads ~168 MB of weights to `~/.cache/hyperframes/background-removal/models/`. + +### Layer separation (`--background-output`) + +Pass `--background-output` (or `-b`) to emit a **second** transparent video alongside the cutout: same source RGB, alpha is `255 − mask` instead of `mask`. The cutout is the subject with a transparent background; the plate is the original surroundings with a transparent hole where the subject was. + +| File | Alpha is… | Use it for | +| -------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------- | +| `-o subject.webm` | The mask — subject opaque, background transparent | Foreground layer, place on top | +| `--background-output plate.webm` | Inverse — surroundings opaque, subject region transparent | Bottom layer; put text or graphics between this and the subject | + +Both outputs share the same `--quality` preset and run from a single inference pass — encode cost roughly doubles, segmentation cost stays the same. Only valid for video inputs and `.webm`/`.mov` outputs. + +**Hole-cut plate, not an inpainted clean plate.** The subject region in `plate.webm` is fully transparent — composite something opaque under it to fill the hole. The single test for whether `--background-output` is the right tool: _will anything ever be visible through the subject's silhouette where the subject used to be?_ + +| Use case | Right tool | +| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| Text/graphics between the cutout and the plate (this command's reason for existing) | **Hole-cut** (`--background-output`) | +| Subject onto an unrelated scene | Just `subject.webm`; ignore the plate | +| Show the room _without_ the person, alone over no other content | **Clean plate** — needs an inpainter (LaMa, ProPainter, E2FGVI). Not this command. | +| Replace the subject with a different subject | **Clean plate** — same as above | + +If a user asks for "the room with the person removed" and intends to display it standalone, do **not** reach for `--background-output`. Tell them they need an inpainter. + +Typical layered composition (the canonical hole-cut use case): + +```html + + + + +

MAKE IT IN HYPERFRAMES

+ + +
+ +
+``` + +This is functionally equivalent to the text-behind-subject pattern below, but you don't need the original `presenter.mp4` in the project — the plate replaces it. Useful when you want to ship just the two transparent layers and let the user drop arbitrary content between them. + +### Output Format + +| Format | When | +| --------------------- | ------------------------------------------------------------- | +| `.webm` (VP9 + alpha) | Default. Compositions play this directly via `