PTENES
MODULE 4.1

πŸ“Ί YouTube & Shorts

Publish an explainer video on YouTube (16:9) and a Short/Reel (9:16) from the same HyperFrames project β€” without recording on camera or using non-linear editing.

6
Topics
25
Minutes
Practical
Level
Application
Type
build-index.mjs node . 16:9 1920Γ—1080 YouTube 9:16 1080Γ— 1920 Shorts --vertical πŸ“„ SCRIPT.md One script. Two renders. node build-index.mjs node build-index.mjs --vertical
1

πŸ–₯️ 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.

Main concept

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.

βœ“ DO in YouTube videos
  • βœ“ Use --quality high in 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
βœ— DON'T for YouTube videos
  • βœ— 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)
16:9 render β€” exact command
# Generates index.html 1920Γ—1080, then renders it
node build-index.mjs &&
npx hyperframes render --quality high \
--output renders/meu-tutorial-16x9.mp4
Key concepts
Fixed viewport
The index.html has width/height hard-coded to 1920Γ—1080β€”the Chrome capture uses exactly those dimensions.
H.264 + FFmpeg
HyperFrames calls FFmpeg internally. The generated MP4 is accepted by YouTube uploads without re-encoding.
GSAP scenes
Each sceneN() returns static HTML; anim() receives the GSAP timeline and animates the elements.
2

πŸ“± 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.

πŸ“Š Dimensions and destinations
16:9 β€” Horizontal
Resolution: 1920 Γ— 1080 px
Destination: YouTube, Vimeo, LinkedIn
Flag: node build-index.mjs (without a flag)
9:16 β€” Vertical
Resolution: 1080 Γ— 1920 px
Destination: YouTube Shorts, Instagram Reels, TikTok
Flag: node build-index.mjs --vertical
πŸ’‘
Adaptive CSS layout

In 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.

βœ“ DO in Shorts/Reels
  • βœ“ 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)
βœ— DON'T for Shorts/Reels
  • βœ— 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)
Key concepts
--vertical flag
Injects a class and rewrites the viewport to 1080Γ—1920 without changing the scene functions.
Safe zone
Reserve ~15% at the top and bottom for app UI β€” keep content in the center only.
Reels vs. Shorts
The same 9:16 MP4 works for Instagram Reels and YouTube Shortsβ€”upload it to both.
60 s limit
Shorts must be ≀ 60 s to appear in the dedicated tab. Longer videos become regular videos.
3

⏱️ 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.

Typical retention timeline
0 – 5 s
Hook β€” Don't Close
5 – 30 s
Quick context
30 – 90 s
Core content
90 – 110 s
CTA β€” conversion
> 180 s
Sharp drop
⚑
The narration is the metronome

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.

πŸ“Š Duration Γ— retention equation
~100s
of speech (TTS pf_dora)
β‰ˆ 1:50
of the rendered video
> 65%
expected average retention
Key concepts
Hook in the First 5s
Scene 1 determines whether the viewer stays. Start with the promise or the problemβ€”never with a brand intro.
Real AUDIO[]
Fill in with the exact durations of ffprobe assets/audio/sN.wav β€” HyperFrames synchronizes animation and audio.
speed 0.98
A speed slightly below 1.0 makes the speech sound more natural in PT-BR with the pf_dora voice.
4

πŸ’¬ 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.

How it works in HyperFrames

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.

Caption style (house style)
/* burned-in caption in the frame */
.caption {
position: absolute;
bottom: 72px; /* above the safe zone */
left: 50%; transform: translateX(-50%);
font-family: 'Inter', sans-serif;
font-weight: 600;
font-size: 36px;
color: #F0EBD8;
background: rgba(0,0,0,0.55);
border-radius: 8px;
padding: 10px 20px;
max-width: 80%; text-align: center;
}
⚠️
A caption is not a file subtitle

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.

πŸ’‘
Vertical: adjust the font-size

In mode --vertical, use .vertical .caption { font-size: 52px; bottom: 160px; } β€” the 1080Γ—1920 resolution is much larger, so the caption needs to scale.

Key concepts
CAPTIONS[]
Array of strings in build-index.mjsβ€”each item corresponds to a scene and is injected into the frame.
Accessibility
Burned-in captions serve deaf users and people in places without audio (bus, work).
translucent rgba
A semi-transparent background preserves the frame's appearance while ensuring minimum WCAG 2.1 AA contrast.
5

🏁 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().

Standard INEMA.CLUB CTA scene

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.

Example prompt β€” invoking the video-explicativo skill
# Paste into Claude Code (video-explicativo skill installed)
Create an explanatory video about "how Kokoro TTS works".
Generate 16:9 and 9:16. CTA for INEMA.CLUB.
pf_dora voice, speed 0.98, PT-BR.
Premium dark palette, amber accent #FFC300.
The skill follows this workflow: SCRIPT.md β†’ init β†’ fetch-fonts β†’ WAV narration β†’ build-index.mjs β†’ lint β†’ render Γ—2.
Where the CTA appears in the video structure
Scenes 1–7: content
Script, examples, animations β€” the real value of the video.
Scene 8: summary
Quick recap (3–5 s) of the key points β€” anchors the memory.
Scene 9: INEMA.CLUB CTA ← required
INEMA.CLUB with glow + URL + short narration. Already included in the template.
βœ“ DO in the CTA
  • βœ“ 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
βœ— DON'T with the CTA
  • βœ— 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
Key concepts
scene9()
Prebuilt function in the template β€” don't rewrite it from scratch; just adjust the domain and glow.
Amber glow
amber drop-shadow #FFC300 pulses on the mark β€” signaling "click here" without needing a button.
Dual channel
Visual URL + narrated URL = bimodal reinforcement. Those who watched with sound and those who watched on mute both catch the destination.
6

♻️ 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.

DRY principle applied to video

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).

Complete render sequence (both formats)
# 1. Render 16:9 β€” YouTube (1920Γ—1080)
node build-index.mjs && \
npx hyperframes render --quality high \
--output renders/kokoro-tts-16x9.mp4

# 2. Render 9:16 β€” Shorts/Reels (1080Γ—1920)
node build-index.mjs --vertical && \
npx hyperframes render --quality high \
--output renders/kokoro-tts-9x16.mp4

# 3. Validate before publishing
npx hyperframes lint # 0 errors
npx hyperframes inspect --samples 16 # 0 layout issues
πŸ’‘
Always render the draft first

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.

Summarized pipeline
πŸ“„
SCRIPT.md
6–9 scenes, ~100s speech
β†’
πŸ”§
build-index.mjs
sceneN() + anim()
β†’
🎬
16x9.mp4
9x16.mp4
ready to upload
βœ“ DO in the dual workflow
  • βœ“ Render 16:9 first, validate, then 9:16
  • βœ“ Keep both MP4s in renders/ with distinct names
  • βœ“ Test --quality draft in both modes before high
  • βœ“ Add selectors .vertical in the scene CSS for long text
βœ— DON'T in the dual workflow
  • βœ— 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
Key concepts
DRY
Don't Repeat Yourselfβ€”a single source code, two outputs. No duplicate maintenance.
renders/
Default output folder. Always name it with the -16x9 and -9x16 suffixes to avoid confusion.
lint + inspect
Two quality passes: lint checks HTML/JS; inspect extracts frames and shows visual overflow.
draft β†’ high
Draft rendering is fast (~20s). High quality can take minutes. Always validate in draft first.

πŸ“‹ Module 4.1 Summary

βœ“ 16:9 (1920Γ—1080) = YouTube. No flag in build-index.mjs.
βœ“ 9:16 (1080Γ—1920) = Shorts/Reels/TikTok. Flag --vertical.
βœ“ ~100s of speech (β‰ˆ 1:50) maximizes retention. pf_dora voice speed 0.98.
βœ“ Burned-in captions: Inter 600, 36px, rgba(0,0,0,0.55). Always on.
βœ“ Required final CTA (scene9) β€” INEMA.CLUB or your brand.
βœ“ One SCRIPT.md β†’ two MP4s. Lint + inspect before publishing.
Next module:
4.2 πŸš€ Onboarding, lessons & launches
Apply HyperFrames to course welcome videos, recorded lessons, and product launchesβ€”with CTA variations for each context.