help quicky produce instagram reels and youtube shorts
94
96%
Does it follow best practices?
Impact
87%
2.12xAverage score across 3 eval scenarios
Passed
No findings from the security scan
Read this when the project mode is music-video and you are building or
editing plan/cut_plan.json. Talking-head plans follow
references/talking-head.md instead.
python3 scripts/build_cut_plan.py work/clips.json work/beats.json \
--target-duration 30 --hook run_01 --close finish_04 --out plan/cut_plan.json--hook and --close take the picks from the contact-sheet review.
The generator uses a phase-based structure, every duration snapped to a real beat timestamp:
| Phase | Cut length | Purpose |
|---|---|---|
| HOOK | ~2-bar hold | grab attention (the contact-sheet pick, via --hook) |
| ESTABLISH | 4-beat | let shots read, set the scene |
| GROOVE | 2-beat | settle into rhythm |
| PUNCH | 2-beat (1 via --punch-beats 1) | energy peak — kept short; walls of sub-second cuts read as "busy" |
| BREATHE | 4-beat | relief after the peak |
| BUILD | 2-beat | re-accelerate into the ending |
| CLOSE | ~2-bar hold | payoff / smile / CTA (--close) |
Even a 1-beat punch phase reads as busy to viewers — resist sub-second cuts.
Built-in guarantees: a 2-beat minimum cut (1-beat flash cuts are opt-in and punch-phase only), in-points spread across each clip's full duration rather than clustered in its first seconds, and no two adjacent segments from the same clip — that last one is what separates a montage from a jump-cut generator.
plan/cut_plan.json is the single human-editable source of truth:
{
"music": "music/track.mp3",
"music_start": 0.0,
"segments": [
{"clip": "work/mezz/run_01.mp4", "in": 2.4, "duration": 1.85, "note": "HOOK — most kinetic shot here"},
{"clip": "work/mezz/gym_03.mp4", "in": 7.4, "duration": 0.94, "note": "groove · 2-beat"}
]
}Hook advice: the first 1.5–2s decides scroll-past.
fetch_music.py saves an .srt beside the track when the video has subtitles.
To open the reel on a specific line:
python3 scripts/build_cut_plan.py work/clips.json work/beats.json \
--music-start-lyric "fire in my bones" --out plan/cut_plan.jsonThe lyric timestamp is where the singer started, which is off-grid; it snaps to
the nearest beat, preferring a downbeat when one is about as close. A line that
appears more than once is refused rather than guessed — use
--lyric-occurrence N. --music-start SECONDS snaps a raw time the same way.
Auto-generated captions mishear a lot, so check the .srt if a line is not
found.
Clips are picked least-used-first, scanned in a per-phase order. Both matter: least-used alone reproduces strict rotation exactly (with no adjacent repeats and even usage there is only one cycle), which is what made long reels sound like a loop. Reshuffling the pool per phase breaks that while keeping usage even. The shuffle is seeded, so the same inputs give the same plan.
--climax CLIP pins a clip at the energy peak, the way --hook and --close
pin the ends.
In-points are positional by default: usage j of K starts about j/K of the
way through the clip.
--score-in-points tries several candidates per segment and keeps the one that
actually contains a person, using Apple Vision through
scripts/detect_subjects.py.
This replaced an edge-density heuristic that did not work. On real go-kart 4K, bare track scored 6.3-12.6 on edge density against 8.6-12.4 for helmet close-ups — the ranges overlap completely, because tarmac carries gravel, kerbs, fences and a horizon. Vision separates the same frames with no overlap: zero detections on every empty frame, 0.65-0.75 confidence on every subject.
Costs roughly half a second per candidate and needs
pip install pyobjc-framework-Vision pyobjc-framework-Quartz (macOS only).
Without it the flag exits with that instruction rather than silently degrading.
It detects people, not interest. POV and scenery footage scores zero throughout, which is correct — the shot is the motion, not a subject. The "no person in any candidate" warning is only meaningful for footage that should show someone.
--pan on normalize_clips.py is per CLIP, so a clip reused at several
in-points gets one framing for all of them — wrong whenever the subject sits on
a different side each time.
For per-cut framing, keep the width at normalize time and crop at render:
python3 scripts/normalize_clips.py work/clips.json --outdir work/mezz --no-cropThen give individual segments a "pan": left, center, right, or a 0..1
fraction.
{"clip": "work/mezz/drone.mp4", "in": 12.4, "duration": 1.9, "pan": 0.7}A cropped mezzanine cannot be reframed later — the pixels are gone, and the pan is silently a no-op. Decide at normalize time. Natively-vertical clips have no spare width either, so pan only does something for sources wider than the target aspect.
Whether the shot is any good. Detection answers "is a person here", not
"is this the right moment". Run audit_plan.py and look.
This is the single biggest source of hand-fixing, so check it every time:
python3 scripts/audit_plan.py plan/cut_plan.json --out work/plan_audit.pngOne labeled thumbnail per segment, sampled at the midpoint. --at in shows
the exact cut frame instead. Fix bad picks in the plan, not after the render.
.tessl-plugin
evals
skills
reel-builder
assets
remotion-cards
references
scripts