Portrait Video Guide
Puffin Ship has three tools for getting the right horizontal crop when converting landscape footage to a portrait output frame. They're designed to be used in combination.
The tools
| Processor | Method | Cost | Accuracy |
|---|---|---|---|
reframe |
Scale to fill portrait height, center crop | Free | Fixed center |
motion_center |
Temporal variance analysis (2fps) | Free | Good for active subjects |
speaker_center |
Claude Vision per clip | API call per clip | Best for presenters |
In the editor
Set the shape of the video first: open Settings from the editor header and pick the 9:16 preset, so the project is portrait before you build anything.
Then add two steps from the Library, both under Transforms. First reframe, with a source input naming your footage. Then either motion_center (free, works from movement) or speaker_center (an AI call per sampled frame, works from finding the person). Neither needs anything else filled in to start.
Neither step adds clips, so nothing new appears in the run timeline. What you check is the preview: play the finished run and see whether the subject sits in the middle of the frame. If they do not, set center_fraction on the centering step, which overrides the automatic guess, and run again. Values below 0.5 move the crop left, above 0.5 move it right.
Step 1: reframe (always required)
reframe scales the source footage to fill the portrait output height. Without it, landscape footage leaves letterbox bars. It centers the crop horizontally; the other two processors then adjust that center.
- id: reframe
processor: reframe
input:
- id: source
file: speaker-footage
cache: true
Step 2: Choose your centering approach
Option A: motion_center (fast, no API)
Finds the highest-motion column in the footage: usually where the speaker is. Best for single-camera footage where the speaker doesn't move much.
- id: center
processor: motion_center
input:
- id: source
file: speaker-footage
cache: true
If the auto-detection is wrong (e.g. the speaker is always at 0.35 of the frame), override with:
params:
center_fraction: 0.35
Option B: speaker_center (accurate, uses Claude Vision)
Samples frames per clip and asks Claude to identify where the speaker is. More accurate for footage where the speaker's position varies across clips, or when motion_center gives wrong results.
- id: center
processor: speaker_center
input:
- id: source
step: ai_cut # apply to the cut, not raw footage
params:
backbone_level: main
frames_per_clip: 3
cache: true
Full portrait pipeline example
pipeline:
- id: transcribe
processor: generate_transcript
input:
- id: source
file: speaker-footage
params:
language: "en"
cache: true
- id: ai_cut
processor: ai_agent
input:
- id: transcript
step: transcribe
- id: speaker
file: speaker-footage
prompt: |
You are an editor making a content cut. The transcript and silence intervals are
already provided in the context block above. Call get_input("speaker") to get the
source filename, then call create_edit_decision for each segment to keep.
Use source=files[0], type="video", level="main". Snap in/out to silence boundaries.
Remove: filler words, long pauses, false starts. Keep: complete sentences.
output:
timeline: main
review: true
cache: true
- id: reframe
processor: reframe
input:
- id: source
file: speaker-footage
cache: true
- id: center
processor: motion_center
input:
- id: source
file: speaker-footage
cache: true
reframe and motion_center/speaker_center both emit TransformPatches that the pipeline applies to all edit decisions using that source file. They don't need to know about ai_cut: they patch based on source file, and all clips referencing that source get the transform.