CtrlK
BlogDocsLog inGet started
Tessl Logo

gamussa/reels-producer-skill

Write talking-head scripts and produce Instagram reels and YouTube shorts

93

1.23x
Quality

94%

Does it follow best practices?

Impact

88%

1.23x

Average score across 3 eval scenarios

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

render-engines.mdskills/reel-builder/references/

Render Engines (ffmpeg, MoviePy, DaVinci Resolve)

Three renderers accept the same plan/cut_plan.json and produce the same kind of file, so they can be compared on identical input.

render_reel.pyrender_moviepy.pyresolve_render.py
Engineffmpeg — per-segment cuts, concat demuxer, one muxMoviePy — in-memory clip graph, single writeDaVinci Resolve Studio — timeline, per-clip transform, native H.264
Dependencyffmpeg onlypip install moviepyResolve Studio running (free refuses scripting)
Speedfastslower — roughly 5x on a short 1080 preview15s for a 28s 1080x1920 reel, measured
Gradingffmpeg filter chainthe same chain, applied as a second passbundled LUT looks on the clip node graph; cine/rapha have no LUT form
Reframeplan's panplan's panplan's pan, or --smart-reframe (subject tracking)

render_reel.py is the default. Only reach for MoviePy when the user asks to compare, or when a plan needs per-clip compositing that the flat concat model cannot express.

Hyperframes is an optional motion-graphics source, not a cut-plan renderer. render_hyperframes.py turns an HTML project into an ordinary clip consumed by any renderer above. See references/hyperframes.md for setup and placement.

Running the comparison

Same plan, same grade, same resolution — change only the script:

python3 scripts/render_reel.py    plan/cut_plan.json --preview --grade rapha --out preview/ab_ffmpeg.mp4
python3 scripts/render_moviepy.py plan/cut_plan.json --preview --grade rapha --out preview/ab_moviepy.mp4

render_moviepy.py prints its wall-clock time so the two are directly comparable. Grading is not reimplemented — MoviePy handles cut, concat, and audio, then the identical GRADES chain from render_reel.py runs as a second ffmpeg pass. That keeps the assembly engine as the only variable.

Ask before switching

Which engine to use is a user-facing choice, not an implementation detail: it changes render time and, slightly, the audio mix. Offer the comparison rather than silently swapping engines mid-project.

Known differences

  • Duration can differ by a frame or two. Concat butts segments together; MoviePy's compose recomputes the timeline.
  • Audio level differs slightly — measured about 2.7dB quieter from MoviePy on the same plan with the same gains. Check loudness before publishing a master rendered by the non-default engine.
  • Preview size is 540x960 from both, ignoring --resolution.

The Resolve engine

python3 scripts/resolve_render.py plan/cut_plan.json --out work/master.mp4 --look gritty
python3 scripts/resolve_render.py plan/cut_plan.json --out work/master.mp4 --smart-reframe --keep-project

Picture comes from Resolve; the audio spine — music, --vo-overlay, or the clips' own sound with --natural-audio — goes through the same mux as render_reel.py, so the sound design does not fork with the engine. The result is an ordinary master: check_cuts.py, then export_variants.py and its compliance table, unchanged.

Reach for it when the reframe should track a subject (--smart-reframe), when handheld footage wants stabilising (--stabilize — Resolve's analysis, no ffmpeg equivalent shipped here), when the footage wants a LUT applied on Resolve's node graph, or when the human will keep working the timeline afterwards (--keep-project, then resolve_bridge.py pull). Measured with both AI passes on 16 clips: about seven seconds of analysis per clip, then a 12s render.

What stays with ffmpeg on purpose:

  • Loudness. Resolve's NormalizeAudioLevel works per clip and our audio is a mix built at render — measured, it drove near-silent drone clips to +30 dB. export_variants.py normalises the mix.
  • Captions, transitions, QC, compliance. No Resolve API adds transitions; captions keep the styled-card look from gen_captions.py.

LUTs must live under Resolve's LUT directory — SetLUT refuses absolute paths — so the bundled looks are copied to <LUT dir>/reel-builder/ on first use. Pass --lut relative to that directory.

Whatever project the human has open is saved before the script switches to its own, and the scratch project is deleted afterwards unless --keep-project.

.mcp.json

tile.json