🧹 npx hyperframes lint
The first quality gate: the linter analyzes your index.html generated and reports structural issues before you spend time rendering. Goal: 0 errors.
The linter reads the index.html generated (not the build-index.mjs) and validates the composition structure. Errors stop the render — warnings are informational.
Always run after node build-index.mjs e before of any render. Lint is fast (<2 s) and does not start Chrome.
overlapping_clipsTwo scene clips have overlapping time intervals. Fix this by adjusting the array AUDIO[] no build-index.mjs — durations must add up in strict sequence.
multiple_root_compositionsMore than one element with data-composition at the root of the document. HyperFrames only accepts one composition per file index.html.
google_fonts_importThe lint detects a @import url('fonts.googleapis.com/...') in the CSS. External fonts are prohibited because headless Chrome has no internet access during rendering. Use node fetch-fonts.mjs to download the .woff2 and serve locally via assets/fonts/fonts.css.
During composition, run node build-index.mjs && npx hyperframes lint each time there’s a significant change to build-index.mjs. Cost: less than 2 seconds. Prevents surprises at render time.
🔍 npx hyperframes inspect --samples 16
The inspector opens headless Chrome, captures 16 frames distributed throughout the video, and audits each one: layout, text overflow, elements outside the canvas. Goal: 0 issues.
- ✓ Add
data-layout-ignorein off-canvas decorative elements (giant background words, glows,.bg-layer) - ✓ Prefer
left/rightinstead ofwidthfor highlight markers - ✓ Test long code in
overflow-x: autowith an explicitly defined width container - ✓ Use
z-index:-1in the glows to prevent them from leaking outside the bounding box
- ✗ Code-line text overflowing the container — shorten it or break it into lines
- ✗ SVG without
viewBoxdefined — the auditor cannot calculate bounds - ✗
position: absolutewithoutoverflow: hiddenin the parent - ✗ Decorative elements without
data-layout-ignore— marked as false-positive overflow
The inspector detects bounding-box overflow, not readability or contrast issues. After 0 problemas in inspect, you still need to visually review frames from the draft render — especially in scenes with text over gradients.
🎬 Draft render — iterate quickly
The mode --quality draft generates a low-resolution MP4 in seconds. Extract one frame per scene with ffmpeg -nostdin and check it visually before spending time on the final render.
Use the scene start time as <t>. For a scene that starts at 12.5 s, use -ss 12.5. The flag -nostdin is required on Windows/git-bash to prevent ffmpeg from consuming stdin and exiting without generating the file.
Claude can view PNG images directly. Extract one frame per scene and check text alignment, overflow, animation placement, and readability against the background. Repeat for every scene.
Edit the build-index.mjs, run node build-index.mjs && npx hyperframes render --quality draft again. The draft loop is cheap—iterate freely before the final render.
| Aspect | Draft | High |
|---|---|---|
| Objective | Visual review | Final deliverable |
| Speed | Fast (<30 s) | 3–4 min / 110s of video |
| Quality | Reduced | Maximum (30 fps) |
| Usage | Iteration loop | Once, at the end |
1 frame per scene works well. For an 8-scene video, that's 8 ffmpeg calls. Focus on transitions and titles — those are the parts most likely to overflow.
👂 Validate the voiceover with the user
Claude doesn’t listen to audio. The user is responsible for validating the narration — and it should happen before from the high render to avoid wasting 3–4 minutes of processing time.
The image analysis tool (Read) works for PNGs and frames, but WAV audio isn’t supported. All voiceover validation must be done by the user listening to the files assets/audio/sN.wav directly.
Before rendering high quality, confirm with the user: correct pronunciation of technical terms, speed (pf_dora --speed 0.98 is the default), natural pauses between scenes, and a duration that fits each scene’s visuals.
- ✓ Listen to each
assets/audio/sN.wavbefore the final render - ✓ Confirm that acronyms were expanded correctly (e.g., "GSAP" → "jee-sap" sounds natural?)
- ✓ Check that the WAV duration matches the visual scene
- ✓ Check via
ffprobeif the measured duration matches theAUDIO[]
- ✗ Don’t render in high quality without listening to the WAVs — costly rework
- ✗ Don't rely only on measured duration — the speech may be truncated
- ✗ Don’t use a speed above 1.05 — the voice sounds metallic and artificial
- ✗ Don’t ignore incorrect pronunciations of English terms in the PT-BR text
Beyond pf_dora (female, recommended default with --speed 0.98), there are pm_alex e pm_santa for male voice variations. If a term is pronounced incorrectly, rewrite it phonetically in assets/txt/sN.txt and regenerate the WAV.
🚀 High render — delivery quality
After a clean lint, an inspect with no overflow, a reviewed draft, and approved narration, it’s time for the final render: --quality high --fps 30. A ~110s video is equivalent to ~3,500 frames and takes 3–4 minutes on a typical machine with 22 cores.
HyperFrames opens Chrome headless at full resolution (1920×1080 or 1080×1920), captures each frame as a PNG, passes them all to FFmpeg, and encodes H.264 with the WAV audio embedded. At 30 fps, 110 seconds = 3,300 frames.
Render time varies with the number of available colors. With 22 colors, expect ~3–4 minutes for 110s of video.
- ✓
npx hyperframes lint— 0 confirmed errors - ✓
npx hyperframes inspect --samples 16— 0 layout issues - ✓ Draft visually checked (1 frame/scene)
- ✓ Voice-over approved by the user
- ✗ Lint still reports errors — it will fail midway through the render
- ✗ Didn’t view any frames from the draft — risk of rework
- ✗ The user hasn’t listened to the WAVs — they may need to re-render
- ✗ O
index.htmlwas not regenerated after the last edit
📐 Generate both formats — 16:9 and 9:16
From the same project, generate the 16:9 (YouTube) and 9:16 (Shorts/Reels) versions in sequence. The critical rule: render right after generating each format — never leave two index.html simultaneous at the root.
O build-index.mjs always overwrites the same one index.html. If you run both modes before rendering, the second overwrites the first and you lose the composition. The correct sequence is: generate → render → generate another version → render.
node build-index.mjs (without a flag)renders/nome-16x9.mp4node build-index.mjs --verticalrenders/nome-9x16.mp4To automate both formats at once, chain them with &&:
O && ensures the 9:16 render starts only after the 16:9 render finishes successfully.
- ✓
node build-index.mjs→ render 16:9 →node build-index.mjs --vertical→ render 9:16 - ✓ Always confirm which one
index.htmlis active before rendering - ✓ Name outputs with the suffix
-16x9e-9x16from the start
- ✗ Run
build-index.mjsebuild-index.mjs --verticalwithout rendering between the two - ✗ Render without
--outputexplicit — can overwrite a previous render - ✗ Use the same output name for both formats
📋 Module 2.5 summary
- ✓
npx hyperframes lint: the 3 fatal errors and how to fix each one - ✓
npx hyperframes inspect --samples 16: overflow,data-layout-ignoreand false positives - ✓ Draft render + frame extraction with
ffmpeg -nostdin -y -ss <t> -vframes 1 -update 1 - ✓ Claude doesn’t listen to audio — the user is responsible for voiceover validation
- ✓ High render:
--quality high --fps 30· ~110s ≈ 3,500 frames ≈ 3–4 min - ✓ 16:9 → 9:16 sequence: generate and render each format before generating the next