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.