Generates pre-valid index.html + composition templates from STORYBOARD.md.
Bakes in: position:absolute, HyperShader wiring, autoAlpha toggles,
SFX placement from manifest, narration audio, grain overlays, scene
visibility management.
Sub-agents only fill creative content — no pipeline mechanics to learn.
Follows the claude-design-hyperframes.md pattern: code handles structure,
AI handles creativity.
Tested: passes lint with 0 errors on first generation.
Snapshot: HyperShader shows a loading overlay ([data-hyper-shader-loading])
while pre-rendering transition frames. Snapshots taken during loading show
"Preparing scene transitions" instead of content. Now waits up to 60s for
the overlay to disappear before capturing.
SVG captioning: Gemini captions for svgs/ subdirectory were generated but
never used in asset-descriptions.md. The generateAssetDescriptions function
had a separate code path for svgs/ that used bare "icon:" labels instead
of checking geminiCaptions. Now checks geminiCaptions first.
CTA beats are consistently under-animated (13 GSAP calls for 8-second
duration in v3 tests). Added explicit guidance: treat CTA with same
animation density as feature beats, minimum 15 GSAP calls, logo entrance
as an event not a fade.
Also clarified that beat count is agent's decision based on content —
no fixed number.
Created manifest.json for all 19 SFX files with exact duration from
ffprobe and Gemini audio analysis description. Updated Step 6 to
reference manifest instead of generic moment-type table.
Root cause of snapshot seeking bug: without position:absolute, beat divs
stack vertically in normal flow. The runtime sets visibility:hidden/visible
to toggle beats, but this only works when beats overlap in the same frame.
Verified: with position:absolute + local CLI, snapshots at t=15 correctly
show beat-4 content instead of beat-1 stuck at every timestamp.
Tested and confirmed: shader transitions work with sub-composition
pipelines. The pattern:
1. Add class="scene" to beat host divs in index.html
2. Load shader-transitions CDN script
3. Call HyperShader.init() with host div IDs and transition configs
4. Engine detects window.__hf.transitions and composites with shaders
Verified with minimal test: cross-warp-morph between two sub-compositions
rendered successfully (layered composite, 180 frames, SDR).
1. Capabilities Discovery: replace descriptive guidance with executable
commands (npx hyperframes list, ls registry/blocks/). Agent was told
to "check" but never ran the commands — now it has exact bash to run
and paste results.
2. SFX decoupled from storyboard: removed SFX listing from Step 4
Capabilities Audit. SFX are now wired in Step 6 Audio Wiring only,
matched to the storyboard's creative moments. Prevents creative
direction from revolving around the SFX inventory.
3. Font @font-face: rewritten to explain the auto-resolve system.
Common fonts (Inter, Roboto, etc.) skip @font-face. Brand fonts
use tokens.json for family mapping. Hashed filenames = Google Fonts
subsets that auto-resolve. Prevents agents from giving up on hashed
filenames.
4. SFX wiring table: added moment-type → SFX mapping so agents match
sound to emotion instead of listing files arbitrarily.
The producer's deterministicFonts.ts pre-bundles 18 font families as
data URIs (Inter, Roboto, JetBrains Mono, etc.). The lint rule was
falsely flagging these as missing @font-face. Now only warns for fonts
not in the pre-bundled FONT_ALIASES list — brand-specific fonts like
GT Walsheim or Aeonik that the producer genuinely can't resolve.
Fonts available via Google Fonts (Path 2) still trigger warnings since
they require network access at render time and may fail in sandboxed
environments.
The vision API rejects image/svg+xml, so SVGs were silently skipped during
Gemini captioning — leaving 97% of icons/logos undescribed. This caused
asset utilization as low as 3.8% on SVG-heavy sites (Huly, Raycast).
Now sends SVG source code as text to Gemini, which reads the XML structure
and describes what it renders. Truncates SVGs >10KB to keep token costs low.
Tested on Huly: 2/52 → 37/52 assets described (3.8% → 71%).
- Fix technique count: "10" → "11" in techniques.md and step-6 (was out of sync)
- Fix VFX registry names: npx hyperframes add --tag html-in-canvas (not bare name)
- Fix step-5 sequencing: remove "update index.html" before it exists in step-6
- Fix shader transitions: inline available shader names instead of repo-only path
- Fix launch-video-2 reference: replace inaccessible file path with inline metrics
- Upgrade Gemini API key from "optional" to recommended with user prompt
Two new lint rules that catch the #1 composition quality failure:
- google_fonts_import: warns on fonts.googleapis.com in <link> or @import
- font_family_without_font_face: warns when font-family CSS has no matching @font-face
Verified against 3 baseline sites (framer, raycast, workos) — correctly detects
all font loading failures found in manual A/B testing.
The property delegation on window.__player used Object.defineProperty
with only a getter, causing "Cannot set property renderSeek which has
only a getter" when Studio's motion-wrapping code tried to reassign
__player.renderSeek with a wrapped version. This cascaded into an
infinite error loop making the timeline unusable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The postMessage state path set `_currentTime` without clamping, while the
direct timeline path already used `Math.min(currentTime, _duration)`. A
final-frame state message with a frame count slightly past the end would
set `_currentTime > _duration`, causing the progress bar (position:
absolute, no overflow guard) to bleed out of the scrubber track and
visually cover the volume button, and the time display to show values
like "0:05 / 0:04".
- Clamp `_currentTime` in `_onMessage` to match the direct timeline path
- Clamp defensively in `updateTime` so the display layer never overflows
- Add `overflow: hidden` + `min-width: 0` to `.hfp-scrubber` as a CSS
safety net; remove now-redundant `border-radius` from `.hfp-progress`
(parent `overflow: hidden` handles clipping to the rounded shape)
- Apply the same `overflow: hidden` fix to `.hfp-volume-slider` for
consistency; remove redundant `border-radius` from `.hfp-volume-fill`
- Add regression test covering the postMessage over-duration case
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add mintlify docs page for contributing blocks/components to the
registry catalog. Rename skill from contribute to contribute-catalog
for clearer intent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Narrow description to disambiguate from hyperframes-registry (install)
and hyperframes (in-project authoring) skills
- Add "adjust" comments on dimensions/duration defaults so agents don't
blindly copy 1920x1080/10s for all composition shapes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a new docs page under Getting Started that links to the
heygen-com/hyperframes-launches repo — open-source HyperFrames
compositions behind HeyGen's product launch videos. Includes a brief
catalog of the 5 projects currently in there, framing on why these are
useful (multi-composition shape, real adapter mix, production-grade
timing), and the LFS-aware clone recipe.
Cross-linked from `docs/examples.mdx`'s Next Steps and from
`docs/community/adopters.mdx`.
Came from a Discord ask via blackNoir (forwarded by James) — users
landing on the docs want to see how the internal team builds their own
videos through HyperFrames; this surfaces that source in one click.
The agent skill loader rejects SKILL.md files whose frontmatter description
exceeds 1024 characters, so remotion-to-hyperframes was being skipped at
startup with a "exceeds maximum length of 1024 characters" warning.
Trimmed the description from 1240 to 896 characters by collapsing the
trigger-phrase examples and tightening prose, while preserving every
trigger / no-trigger guardrail. Moved the detailed list of trigger phrases
and the 4 negative cases into a new "## When to use" section in the body
so the guidance is not lost.
Fixes#688
New skill that guides users through the full workflow of contributing
caption styles, VFX blocks, transitions, and other components to the
HyperFrames registry.
Covers: scaffolding, proven patterns (seeded PRNG, paused timelines,
hard kills, unique ID prefixes), validation, rendering, and PR prep.
Includes copy-paste HTML templates for caption and Three.js components
with all the non-negotiable rules baked in.
Tested by an unbiased agent with zero prior HyperFrames knowledge —
built a working cap-typewriter component that passed lint and validate
on the first attempt. Feedback incorporated: per-character animation
guidance, monospace font size exceptions, positioning variants, and
PREFIX naming convention table.
GitHub strips autoplay/loop from <video> tags in markdown, so the MP4
required a click to play. WebP autoplays via <img> while preserving full
HD source quality (1280x720 vs the previous GIF's 400x225).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Clips whose compositionStart is ahead of the current timeline position
were starting immediately because sourceNode.start() always received
when=0. Use the AudioContext scheduling API to defer future clips:
sourceNode.start(ctx.currentTime + delay, mediaStart).
Closes#674
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the two-clock architecture (GSAP rAF ticker + HTMLMediaElement
pipeline reconciled by a 50ms polling loop) with a single TransportClock.
GSAP is always paused and seeked to clock.now() on each rAF tick.
Drift between visual timeline and audio is structurally impossible.
Architecture:
TransportClock.now() ──rAF──▶ timeline.seek(t) + el.currentTime
▲
AudioContext.currentTime (~21µs) ← WebAudio active
OR
audio.currentTime (~33ms) ← HTMLMediaElement fallback
OR
performance.now() (~1ms) ← no audio
Key changes:
- TransportClock class with monotonic + audio-master clock sources
- WebAudioTransport: routes audio through AudioBufferSourceNode for
sample-accurate scheduling, falls back gracefully to HTMLMediaElement
- rAF tick loop replaces 50ms setInterval poll; GSAP always paused
- Strict sync (40ms threshold, consecutive-sample gated) + forceSync
on play/pause/seek transitions for sub-frame media accuracy
- Buffer-stall: visuals freeze when audio is buffering instead of
running ahead
- Frame quantization preserved in seek/renderSeek (parity contract)
Browser-verified: 0.0ms drift after 40 pause/play cycles (was 400ms+).
Also fixes: CDN script HTML error responses in validate (pre-existing).
54 tests across clock, clock-drift, webAudioTransport, and media.
Closes#668