MontajMontajdocs

Clips (Long-form → Vertical)

Turn one long horizontal recording into a series of short, independently-editable vertical (9:16) clip projects — each pre-reframed and ready for the overlays pass.

Clips (Long-form → Vertical)

The clips workflow takes one long horizontal video — a podcast, talk, stream, or interview — and fans it out into N independently-editable vertical (9:16) clip projects. Each output project is a self-contained moment, pre-reframed for portrait, with its own workspace ready for the per-clip overlays pass.

Unlike every other Montaj workflow, clips does not produce a finished video. It produces N new projects. Think of it as the upstream pass that prepares material for per-clip editing, rather than the editing workflow itself.

Use clips when your source is long-form and you want multiple short clips from it. Use overlays or short_captions when you already have individual clips and want to edit each one top to bottom.


Creating a Clips Project

The clips workflow requires a source video (requires_clips: true). Do not use --canvas — the source file is required.

CLI

montaj run --workflow clips --prompt "find the 5 best moments, zoom framing" /path/to/recording.mp4

The workflow runs three steps on the source in order: probe → transcribe → find_clips. The transcribe step uses model medium.en. The source pass only transcribes — it does not clean or trim the source video.


How the Agent Works

find_clips is an agent-authored task (not a CLI executable). After the source steps complete, the agent:

  1. Reads the transcript and probe output.
  2. Identifies N self-contained moments — clean entry and exit, no overlap between clips.
  3. Picks a vertical framing mode per clip (see Vertical Reframing Modes below).
  4. Creates one child project per clip by calling project/init.py directly as a subprocess (the HTTP /api/run endpoint does not expose the required flags). Key flags: --symlink-clips (stages the source as a symlink, not a copy — sources are large), --derived-from <source_project_id>, --normalize lazy, and --resolution 1080x1920.
  5. Sets inPoint/outPoint and sourceCrop on each child's tracks[0][0].
  6. Runs normalize_window per clip window and sets normalizedSrc on each clip item.
  7. Relocates the source file into a shared store at ~/Montaj/.sources/<id>/, repoints each child's symlinked src there, then deletes the source project. Only the N clip projects remain when the workflow finishes.
  8. Asks whether to continue with the per-clip overlays pass now, or hand off — emitting a ready-to-paste prompt per clip if handing off.

Vertical Reframing Modes

ModeDescriptionBest for
zoomCentered 9:16 crop filling the full frameSingle speaker, talking-head, tight framing
thirdsSource floated in the top portion of the 9:16 canvas over a background fillDemos, wide shots, content where spatial context matters
mixCropped source at ~50% scale in the top half, bottom open for captions/overlaysWhen you want a visible caption zone below the video

The agent picks a mode per clip based on the content and the editing prompt. You can override in the per-clip overlays pass.


The sourceCrop Primitive

sourceCrop is a field on video items in tracks[0]. It defines which rectangle of the source footage to use, expressed as fractions of the source's natural dimensions:

{
  "id": "clip-0",
  "type": "video",
  "src": "/abs/path/to/recording.mp4",
  "inPoint": 142.0,
  "outPoint": 187.5,
  "start": 0.0,
  "end": 45.5,
  "sourceWidth": 1920,
  "sourceHeight": 1080,
  "sourceCrop": { "x": 0.3418, "y": 0.0, "w": 0.3164, "h": 1.0 },
  "normalizedSrc": "/abs/path/to/project/clip-0_norm.mp4"
}

All four keys (x, y, w, h) are required when sourceCrop is present. sourceWidth and sourceHeight (source pixel dimensions from the probe step) are required alongside sourceCrop so the renderer can compute the crop correctly.

For a centered 9:16 crop from a 16:9 1920×1080 source: x ≈ 0.3418, y = 0, w ≈ 0.3164, h = 1.0.

Important (v3.2.0): sourceCrop (which rectangle of the footage to use) is separate from position and zoom. offsetX/offsetY/scale control where the clip sits in the output frame — these are distinct fields. The editor's crop tool writes sourceCrop; dragging the clip on the canvas writes offsetX/offsetY/scale.


Source Storage and derivedFrom

Sources are never copied into each clip project — the agent symlinks them. After fan-out, the source file is relocated to ~/Montaj/.sources/<source_project_id>/ and the source project itself is deleted. Only the N clip projects remain.

Each child project carries a derivedFrom field set to the source project's UUID:

{
  "derivedFrom": "a3f8c2d1-..."
}

This is a provenance tag — informational only. The source project is gone; derivedFrom records where the clip came from.


Lazy Normalization

Clips projects set settings.normalize: "lazy". This means the full source is not re-encoded at import (source files can be gigabytes). Instead, each clip window is normalized on demand via normalize_window and cached as normalizedSrc — a re-encode covering exactly [inPoint, outPoint] starting at t=0.

Render and preview prefer normalizedSrc over src when present. The engine rebases inPoint/outPoint automatically when using normalizedSrc.

This keeps disk usage flat regardless of how many clips are extracted — each window is encoded once, on first use.


Standard Editing vs. Clips Child Project

Standard editing projectClips child project
Source storageIn project directorySymlinked to ~/Montaj/.sources/<id>/
sourceCropAbsent (unless manually set)Always set (by find_clips)
normalizedSrcOptionalAlways set (by normalize_window)
derivedFromAbsentSource project UUID
settings.normalize"eager" (default)"lazy"
Source projectN/ADeleted after fan-out