| name | composite-split-screen |
| description | compose two video clips side-by-side or stacked into a single 9:16 (or any aspect) frame using ffmpeg. Use when you need the creator-native split-screen layout — e.g. pairing a reaction with a product demo, or stacking a talking-head over b-roll — and want a generic, format-agnostic compositor that is not tied to a specific ad template. |
composite-split-screen
Purpose
Combine two video clips into one frame — either stacked vertically (top-bottom) or side-by-side horizontally (left-right). The output is a single re-encoded .mp4 sized to the requested aspect ratio (default 9:16, i.e. 1080×1920). Duration equals the shorter of the two input clips.
This is a generic compositor: it knows nothing about brand zones, dividers, or avatar placement. Those concerns live in higher-level molecules (e.g. create-split-screen-creator-ad). This atom's job is one thing — geometric composition.
Three fill modes are supported for each zone:
crop — scale the clip to fill the zone, cropping overflow (cover-fill).
blur — fit the clip inside the zone and fill the remaining space with a blurred, cover-scaled version of the same clip (the "blur pill" look common in 9:16 repurposing).
pad — fit the clip inside the zone and pad the remaining space with solid black.
Inputs
--clip-a (required) — path to the first clip. In top-bottom layout this occupies the top zone; in left-right it occupies the left zone.
--clip-b (required) — path to the second clip.
--layout (optional, default top-bottom) — top-bottom (vstack) or left-right (hstack).
--ratio (optional, default 0.5) — fraction of the frame that clip A occupies (0 < ratio < 1). E.g. 0.6 gives clip A 60% of the height in top-bottom mode.
--fill (optional, default crop) — crop, blur, or pad. Applies to both zones.
--aspect (optional, default 9:16) — output canvas aspect ratio as W:H. Common values: 9:16 (1080×1920), 1:1 (1080×1080), 16:9 (1920×1080).
--audio (optional, default a) — which clip's audio to use: a, b, or mix (equal-level stereo mix of both).
--output (required) — output .mp4 path.
Workflow
- Verify
ffmpeg/ffprobe are on PATH and both clips exist.
- Derive canvas dimensions from
--aspect (base width 1080, height derived from ratio).
- Compute zone dimensions: zone A gets
ratio × frame_dimension; zone B gets the remainder.
- Build an
ffmpeg -filter_complex graph:
- Scale each clip to its zone using the chosen
--fill mode.
- Compose with
vstack (top-bottom) or hstack (left-right).
- Mix or select audio per
--audio.
- Encode with
libx264 / yuv420p / aac, duration clamped to min(dur_a, dur_b).
- Write
manifest.json to the output file's parent directory.
Run:
python3 skills/atoms/editing/composite-split-screen/scripts/composite_split.py \
--clip-a /path/to/clip_a.mp4 \
--clip-b /path/to/clip_b.mp4 \
--layout top-bottom \
--ratio 0.5 \
--fill crop \
--aspect 9:16 \
--audio a \
--output /path/to/output.mp4
Output
- A single
output.mp4 (re-encoded H.264 / yuv420p / AAC).
manifest.json in the same directory as the output, containing: skill_name, run_id, inputs (clip_a, clip_b), layout, ratio, fill, aspect, canvas (WxH), duration_seconds, audio_mode, output_file, status, warnings, errors.
Note: output duration = min(duration_clip_a, duration_clip_b) — this is recorded in the manifest.
Quality Checks
- Output file exists and is non-zero size.
ffprobe reports the output dimensions match the requested canvas (e.g. 1080×1920 for 9:16).
- Duration ≈
min(dur_a, dur_b) within ±0.3s.
- Video codec is H.264 (yuv420p); audio codec is AAC.
manifest.json exists with status: "pass" and output_file path resolves on disk.
Failure Modes
- Clip not found — script exits with a clear error before encoding.
ffmpeg not installed — install via brew install ffmpeg.
- Clips have no audio — output is produced without audio; recorded in
warnings. If --audio mix is requested but one clip has no audio, falls back to the clip that has audio.
- Very short clips (< 0.1s) — script exits with an error; duration clamping would produce an empty file.
- Non-standard aspect string — must be
W:H with positive integers; script exits with a parse error.
- ratio ≤ 0 or ≥ 1 — script exits with a validation error.