π₯οΈ 16:9 video for YouTube
The standard YouTube format is 1920 Γ 1080 px. With HyperFrames, you write scenes in HTML + GSAP and render a high-quality MP4βwithout opening a video editor.
The generator build-index.mjs writes a index.html with a fixed viewport at 1920 Γ 1080. HyperFrames uses headless Chrome to capture each frame, and FFmpeg assembles the MP4. Without a flag, the output is always 16:9.
- β Use
--quality highin the final render - β Keep the narration at β 100s of speech (β 1:50 video)
- β Include a high-contrast thumbnail in scene 1
- β Generate MP4 with H.264 codec, smooth 60 fps
- β Publish render
--quality draft(low resolution) - β Exceeding 15 min without chapters (retention drop)
- β Use a font smaller than 32px (illegible on mobile)
- β Omit the CTA scene (conversion loss)
sceneN() returns static HTML; anim() receives the GSAP timeline and animates the elements.π± Shorts/Reels/TikTok in 9:16
The same HTML project becomes a video 1080 Γ 1920 px with the flag --vertical. HyperFrames recomposes the layout and renders the tall frameβwithout duplicating code.
node build-index.mjs (without a flag)node build-index.mjs --verticalIn mode --vertical, HyperFrames injects the class .vertical no <body>. Use selectors .vertical .sua-classe in the scene CSS to reposition elements β column instead of row, larger text, smaller margins.
- β Use fonts β₯ 48px in vertical mode (smaller safe zone)
- β Keep the CTA and caption in the lower half of the screen
- β Validate the layout with
npx hyperframes inspect --samples 16 - β Limit to β€ 60 s for Shorts/Reels (β€ 180 s for TikTok)
- β Reuse the 16:9 layout without adjustments
.vertical - β Put important text in the top band (covered by the app UI)
- β Skip linting before rendering β layout errors appear magnified
- β Publish without captions (most people watch without sound)
β±οΈ Why ~110s keeps viewers engaged
100 seconds of speech equals about 1:50 of video. This is long enough to teach a complete concept and short enough to hold attentionβthe sweet spot for Shorts and YouTube tutorials.
Each scene in SCRIPT.md has β 15β18 seconds of narration (measured with ffprobe). With 6β7 scenes, you get exactly ~100 s of speech. Voice: pf_dora --speed 0.98 in Kokoro. Store the durations in the array AUDIO[] of the build-index.mjs.
ffprobe assets/audio/sN.wav β HyperFrames synchronizes animation and audio.π¬ Captions always on
85% of videos in feeds are watched on mute. Burned-in captions in the frame ensure accessibility and readability even without soundβand also reinforce the premium dark visual identity.
The array CAPTIONS[] in build-index.mjs defines text synchronized with the audio. The template injects a <div class="caption"> fixed at the bottom of each scene. The default CSS uses Inter 600, 36px, rgba(0,0,0,0.55) background, 12px padding β readable on any background.
The HyperFrames caption is burned-in β itβs part of the video frame. Thatβs intentional: it ensures it appears on any platform, even when the player is muted and doesnβt support SRT/VTT. For SEO, also add a separate caption file when uploading to YouTube.
In mode --vertical, use .vertical .caption { font-size: 52px; bottom: 160px; } β the 1080Γ1920 resolution is much larger, so the caption needs to scale.
π CTA at the end
The last scene is the CTAβCall to Action. In the INEMA.CLUB standard, it displays "CONTINUES AT" + a prominent domain + a readable URL. It comes ready in the HyperFrames template as scene9().
The final scene shows "CONTINUES IN" + INEMA.CLUB with an amber glow, URL π inema.club and short narration: "This is INEMA dot CLUB content. Go to: inema dot club.". It is the scene9() in the template β don't remove it.
- β Keep the CTA scene in ALL videos
- β Readable URL and URL spoken in the narration
- β Adapt it to your brand (replace INEMA.CLUB with your domain)
- β Keep the amber glow β consistent visual identity
- β Remove the final scene (loses conversion and identity)
- β Use a long CTA (> 8 s) β viewer abandons
- β Unreadable URL or background that competes with the text
- β Forget to say the URL in the narration
β»οΈ One script, two formats
A single SCRIPT.md + a single build-index.mjs produce both MP4s. This is the complete HyperFrames publishing workflowβno duplicated code.
Write the script once. Adapt the scene CSS with selectors .vertical. Run the generator twice. The result is two MP4s ready to publish on YouTube (16:9) and YouTube Shorts / Instagram Reels / TikTok (9:16).
Use --quality draft to check timing and layout before spending time on the final render. Extract frames with npx hyperframes inspect --samples 16 and review the 16 screenshots before running --quality high.
- β Render 16:9 first, validate, then 9:16
- β Keep both MP4s in
renders/with distinct names - β Test
--quality draftin both modes before high - β Add selectors
.verticalin the scene CSS for long text
- β Maintain two separate build-index files (violates DRY)
- β Publish without linting β layout bugs show up in the player
- β Overwrite renders/ without versioning
- β Ignore the safe zone in vertical mode
π Module 4.1 Summary
--vertical.