ralphy render <project-id> is the only render path. It reads scenario.json and asset-manifest.json, drives HyperFrames (Puppeteer + FFmpeg), and writes the result to workspace/projects/<id>/render/final.mp4. No direct bunx hyperframes render calls, no ad-hoc ffmpeg shells — every recipe lives behind a ralphy verb so the cost rollup, the gen-log, and the asset manifest all stay consistent. That’s AGENTS invariant #2.
What render does
A render call walks through the project in order:
- Reads
workspace/projects/<id>/scenario.jsonandasset-manifest.jsonto know which slots exist and where their files live. - Resolves the composition.
workspace/projects/<id>/index.htmlis the root composition HyperFrames seeks deterministically via its paused GSAP timeline. - Calls HyperFrames headlessly. Puppeteer rasterizes each frame, FFmpeg muxes the mp4.
- Writes the result to
workspace/projects/<id>/render/final.mp4(or wherever you pointed--output). - Optionally post-processes with ffmpeg if
--loudnormis set — EBU R128 two-pass normalization to -16 LUFS. - Appends an entry to
generations.jsonlwith the modelhyperframes, the latency, and the output path. Renders are first-class citizens in the gen-log.
The flags that matter
Output mp4 path. Default
workspace/projects/<id>/render/final.mp4.Frames per second. Default
30. Bump to 60 for high-motion content.Render quality preset (
draft, standard, high). Default standard.Resolution preset (
portrait, landscape, square) — drives the canvas DPR.Apply EBU R128 loudnorm post-render via ffmpeg. Target -16 LUFS, two-pass. The TikTok / Reels / Shorts loudness standard. On by default for batch renders; off by default for single renders so you can iterate faster.
Print the resolved render plan (composition path, output path, flags) and exit. No render.
The loudnorm pass
TikTok, Reels, and Shorts all target -16 LUFS integrated loudness. A video that’s quieter sounds amateur; a video that’s louder gets compressed to mush by the platform’s normalizer.--loudnorm runs a two-pass EBU R128 loudnorm via ffmpeg after HyperFrames writes the mp4:
cli/lib/ffmpeg-recipes.ts — the only sanctioned place ffmpeg gets called from. Never run ffmpeg directly on a project file; use ralphy audio loudnorm or pass --loudnorm on render.
If you forgot --loudnorm and want to normalize an existing render:
The iteration loop
The normal cadence:- Render with
--dry-runfirst if you want to see the plan. - Render for real. Wait until HyperFrames + ffmpeg finishes.
- Watch the mp4. Decide what’s off — wrong scene timing, captions out of sync, hook too slow, music too loud.
- Iterate on the underlying assets via the art-director playbook (regenerate a slot) or the scenarist playbook (rework a scene’s pacing).
- Render again. The new render lands at
final.<iter>.mp4so the previous take is preserved.
Preview vs final render
There’s no auto-launched preview in Ralphy. AGENTS invariant #5: no background processes, chat is the interface. If you want to preview frames live, run it foreground in a separate terminal:ralphy render.
Perf targets
docs/perf-targets.md sets two numbers:
- Cold-start single video ≤ 8 minutes end-to-end (intake → render).
- Batch of 10 ≤ 25 minutes total.
Troubleshooting
Common render failures break down by stage:E_NOT_FOUNDon the project. The project ID is wrong, or you’re in the wrong workspace. Checkralphy project list -p.- Manifest references a missing asset. Re-run
ralphy project verify <id>to see what’s broken. The fix is usually to regenerate the missing slot. - HyperFrames lint fails. Missing
class="clip", missingdata-composition-id, timeline registration key mismatch. Runbunx hyperframes lint <project>to see the exact issue. - ffmpeg loudnorm fails. Usually a corrupt mp4 from a failed render. Inspect with
ffprobe, then re-render. - The render is silent. Check that
asset-manifest.jsonhas voiceover slots and that they point at non-empty files. The composition mounts them via<audio data-start="..." data-volume="...">; an empty file becomes silence.
ralphy doctor — see Setup and doctor.
Related
- Reviewing and iterating — what to do between renders
- Generating assets — what feeds the render
- CLI: rendering verbs — every flag, full reference
- CLI: error catalog — render-stage errors
editor.md— the playbook this page paraphrasesdocs/perf-targets.md— the perf budget