Export
The three export engines, which range gets exported, and the ffmpeg encoding profiles.
Export is user-driven (Export button → dialog). Three engines:
- 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. - WebCodecs - same frame-accurate compositor loop, but the browser’s
VideoEncoderproduces 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-classVideoEncoderwithavc: { 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). OptionalbitrateMode: "quantizer"(fixed QP) exists in the spec but is rarely supported by hardware encoders with Annex-B. Unavailable while anexportFramecrop is set - use Fast for cropped delivery. - 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.
- Entire timeline (or no markers) → the whole timeline
- IN–OUT, IN only → from
inPointto the end - IN–OUT, OUT only → from the start to
outPoint - 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>- There is no allow-list. Any codec, filter, container or flag your local ffmpeg supports works - ProRes, DNxHD, NVENC/QSV/VideoToolbox, VP9/AV1, 10-bit, HDR tags. See the shipped
prores422andbroadcast1080i50profiles. - Args are validated by ffmpeg itself, not by a schema: when an export starts the server dry-runs the profile against a 0.1 s synthetic input (
lavfi). A typo or an encoder your build lacks is rejected up front with ffmpeg's own message, instead of failing after every frame has been rendered. - Use the array form - each element is passed to
spawnuntouched, so no quoting is needed (["-vf", "drawtext=text='hi there'"]just works). A plain string is accepted and split on whitespace. colorcontrols JPEG→YUV conversion and stream tags (-colorspace/-color_primaries/-color_trc/-color_range). Default is BT.709 limited (range: "tv"). Use"range": "pc"for full-range masters. Do not put those flags inargs- the engine strips them and appliescolorso vf and tags stay aligned.-pix_fmtstays inargs(sampling / bit depth only).- Nothing else is injected for you:
+faststart,-shortest,-strict -2for Opus in MP4 and pixel-format choices are all yours to write. - Frames arrive as JPEG (4:2:0), so
yuv422p/yuv444pcannot recover chroma the source never had; raisejpegQualitybefore reaching for a wider pixel format. OptionalpixelFormat:"rgba"on/api/export/beginpipes uncompressed canvas frames instead (-f rawvideo) when a caller wants full chroma. - The audio mix is only present when the timeline has audio; with no audio there is a single input, so avoid hardcoded
-map 1:a.
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):
- Profile picked in the Export dialog (one-off; saved to browser settings unless overridden)
project.encodeProfile- set via UI reload or{op:"setProject", set:{encodeProfile:"hq"}}- Browser setting
encodeProfilein localStorage (set when you change the Export dropdown) defaultinencoding-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.