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. Intop-bottomlayout this occupies the top zone; inleft-rightit occupies the left zone.--clip-b(required) — path to the second clip.--layout(optional, defaulttop-bottom) —top-bottom(vstack) orleft-right(hstack).--ratio(optional, default0.5) — fraction of the frame that clip A occupies (0 < ratio < 1). E.g.0.6gives clip A 60% of the height intop-bottommode.--fill(optional, defaultcrop) —crop,blur, orpad. Applies to both zones.--aspect(optional, default9:16) — output canvas aspect ratio asW:H. Common values:9:16(1080×1920),1:1(1080×1080),16:9(1920×1080).--audio(optional, defaulta) — which clip's audio to use:a,b, ormix(equal-level stereo mix of both).--output(required) — output.mp4path.
Workflow
- Verify
ffmpeg/ffprobeare 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_complexgraph:- Scale each clip to its zone using the chosen
--fillmode. - Compose with
vstack(top-bottom) orhstack(left-right). - Mix or select audio per
--audio.
- Scale each clip to its zone using the chosen
- Encode with
libx264 / yuv420p / aac, duration clamped tomin(dur_a, dur_b). - Write
manifest.jsonto 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.jsonin 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.
ffprobereports the output dimensions match the requested canvas (e.g. 1080×1920 for9:16).- Duration ≈
min(dur_a, dur_b)within ±0.3s. - Video codec is H.264 (yuv420p); audio codec is AAC.
manifest.jsonexists withstatus: "pass"andoutput_filepath resolves on disk.
Failure Modes
- Clip not found — script exits with a clear error before encoding.
ffmpegnot installed — install viabrew install ffmpeg.- Clips have no audio — output is produced without audio; recorded in
warnings. If--audio mixis 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:Hwith positive integers; script exits with a parse error. - ratio ≤ 0 or ≥ 1 — script exits with a validation error.