# Jump Cut

> Manufacture "multi-cam" hard cuts on static talking-head stretches by alternating a base framing with tighter punch-in reframings of the SAME speaker, each cut snapped to a word start so it lands on speech. Deterministic, no Claude. Timeline-preserving — audio is copied untouched and total duration is identical, so every downstream timestamp (captions, b-roll windows) stays valid. Runs on the 1080x1920 vertical clip AFTER fill-vertical and BEFORE zoom-punch/broll/burn-subtitles, so cutaways override their own windows and captions burn on top. The visual-change-density lever for sources where b-roll stays sparse.

- Skill: `jperrello/jump-cut` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add jperrello/jump-cut`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jperrello/jump-cut/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: jperrello (https://skillmd.com/u/jperrello)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jperrello/jump-cut

---


# jump-cut

Single-camera podcast footage reads as one long static crop between cutaways.
Top short-form editors break that up with frequent hard cuts between framings of
the same speaker — a fake "multi-cam" rhythm that keeps the frame changing even
with no new footage. This skill manufactures that churn deterministically.

## Invoke

```
.claude/skills/jump-cut/jump-cut.sh <in.mp4> <transcript.json> <out.mp4> [seg_secs=3.2]
```

- `in.mp4`: the 1080x1920 vertical clip (post fill-vertical)
- `transcript.json`: the clip-local word-timed transcript (cuts snap to word starts)
- `seg_secs`: target segment length / cut rhythm (default 3.2s)

## How it works

- `plan.py` tiles `[0, dur]` into segments at ~`seg_secs` spacing, each boundary
  snapped to the nearest word start within 0.7s so the cut lands ON speech.
- Segments alternate **base** (full frame, unchanged) and **punch** (a centered
  ~1.11–1.16x reframe biased to the upper third). The cold-open lead (2.6s, the
  title) and the tail (1.5s, the landing) stay base for a stable open/close.
- One ffmpeg pass: `split` → per-segment `trim`+`crop`/`scale` → `concat`. Video
  is concatenated; **audio is mapped straight from the source with `-c:a copy`**,
  so duration and every downstream timestamp are preserved exactly.

## Placement rules

- count = clamp by `seg_secs` rhythm, capped at `JUMP_CUT_MAX` (default 8) cuts
- clips shorter than `JUMP_CUT_MIN` (default 13s) → passthrough (no churn)
- first 2.6s (cold-open title) and last 1.5s excluded
- min 1.6s between cuts after snapping (never seasick)

## Pipeline order

`fill-vertical` → **`jump-cut`** → `zoom-punch` → `chunk-captions` → `broll-pick`
→ `broll-composite` → `burn-subtitles` → …

It must touch only the clean vertical: b-roll cutaways replace the full frame in
their windows (overriding the reframe underneath) and captions burn on top, so
neither warps. Non-fatal (any failure → passthrough), idempotent (`.jcmeta`
mtime+param signature), `JUMP_CUT=0` disables.

## Env

- `JUMP_CUT` — `0` disables (passthrough). Default on.
- `JUMP_CUT_SEG` — cut rhythm in seconds (default 3.2; also positional arg 4).
- `JUMP_CUT_MAX` — max reframe cuts per clip (default 8).
- `JUMP_CUT_MIN` — min clip duration to activate (default 13.0s).
- `JUMP_CUT_LEAD` / `JUMP_CUT_TAIL` — stable open/close reserve (default 2.6 / 1.5).

