Export

The three export engines, which range gets exported, and the ffmpeg encoding profiles.

Export is user-driven (Export button → dialog). Three engines:

  1. Fast - browser renders each frame with the normal compositor (SVG, keys, AI masks), streams JPEGs + an offline WAV mix to the server; a single ffmpeg pass encodes them via an encoding profile into ./exports/. Quality / software path; keeps rendering if you switch tabs.
  2. WebCodecs - same frame-accurate compositor loop, but the browser’s VideoEncoder produces Annex-B H.264 (Main 4:2:0) and the server stream-copies (-c:v copy) while muxing the WAV. Faster uploads, less server CPU. Requires Chromium-class VideoEncoder with avc: { format: "annexb" } plus ffmpeg. Encoding profiles do not apply (the bitstream is already encoded). No ffmpeg-style CRF - quality is bitrate + VBR/CBR (export dialog; remembered in localStorage). Optional bitrateMode: "quantizer" (fixed QP) exists in the spec but is rarely supported by hardware encoders with Annex-B. Unavailable while an exportFrame crop is set - use Fast for cropped delivery.
  3. Realtime (MediaRecorder) - automatic offline fallback when the server, ffmpeg, or WebCodecs is unavailable. Plays the timeline once and records it; keep the tab focused.

The exported span is chosen in the Export dialog (Range: Entire timeline / IN–OUT). IN–OUT is the default when inPoint / outPoint are set; pick Entire timeline to keep those markers for split/trim. Effective IN/OUT export bounds are clamped to projDur so an IN past the last clip cannot produce a one-frame black file.

  1. Entire timeline (or no markers) → the whole timeline
  2. IN–OUT, IN only → from inPoint to the end
  3. IN–OUT, OUT only → from the start to outPoint
  4. IN–OUT, both → the range between them

Claude cannot trigger export headlessly - the compositor lives in the browser; ask the user to click Export, or render with ffmpeg directly from media/ sources if a file is needed.

Encoding profiles encoding-profiles.json#

User-editable at the repo root. A profile is a raw ffmpeg argument list plus the things that are not ffmpeg arguments: jpegQuality (browser frame quality), extension (output container), and optional color (output matrix / range tags). Edit the file while the server runs - the UI hot-reloads the profile list via an SSE profiles event (no full project reload). Fast export only; WebCodecs ignores them.

{
  "default": "delivery",           // profile id used when nothing else is set
  "profiles": {
    "draft": {
      "label": "Draft · H.264 fast",
      "description": "Quick preview — smaller file, faster encode.",
      "jpegQuality": 0.85,         // browser JPEG frame quality (0.1–1)
      "extension": ".mp4",
      // Output color (optional — defaults to BT.709 limited/tv). Independent of
      // -pix_fmt: yuv420p and yuv422p10le both typically use bt709 for HD SDR.
      "color": { "matrix": "bt709", "primaries": "bt709", "trc": "bt709", "range": "tv" },
      "args": ["-c:v", "libx264", "-preset", "veryfast", "-crf", "23",
               "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k",
               "-movflags", "+faststart", "-shortest"]
    }
  }
}

Export is one ffmpeg pass. The server owns the input side and the output path; args is everything in between (plus JPEG color conversion derived from color):

ffmpeg -y -f image2pipe -framerate <fps> -i - [-i audio.wav] <jpeg-color> <args…> exports/<name><extension>

Note: Fast export pipes composited JPEG frames from the browser (-f image2pipe), not -i source.mp4. Filters and codec settings apply to that frame stream. To transcode an existing file verbatim, run ffmpeg directly - that is outside the compositor path.

Which profile is used (priority):

  1. Profile picked in the Export dialog (one-off; saved to browser settings unless overridden)
  2. project.encodeProfile - set via UI reload or {op:"setProject", set:{encodeProfile:"hq"}}
  3. Browser setting encodeProfile in localStorage (set when you change the Export dropdown)
  4. default in encoding-profiles.json

MCP: fablecut_encode_profiles lists profiles; {detail:true} includes each args array; {profile:"hq"} returns one profile. fablecut_status shows the effective profile.