MCP tool reference
All 167 tools OpenFrame offers AI agents over MCP, with every parameter and an example call, generated from the app's own tool definitions.
From docs/MCP tools.md in the repository, rendered when the site is built.
Every tool the editor offers an AI model over MCP, 167 in all, in the order a video gets made. How to connect is in Automation; the playbooks are in Agent playbook. Every edit is undoable; times are seconds; ids come from describe_project.
To connect an agent, see AI and MCP. The agent playbook is how a model should use these tools to make an ad, an epic edit or a short.
What the server tells a model when it connects
You are editing a video in the user's video editor (OpenFrame); every edit shows live and is undoable. Call help first: the steps that make a video, tool by tool; help with topic ad, epic_edit, voiceover or social_short is a playbook, with a tool's name its parameters and an example. Times are seconds; positions are fractions of the canvas from its center (x right, y down); rectangles (box, region) are a top-left corner and a size, fractions of the picture. Work in a new_project: the user's own projects stay untouched.
Find a tool
167 tools in 14 groups
Start here 3
Projects 7
Media 7
Timeline 37
add_media_clipadd_color_clipadd_adjustment_clipadd_trackupdate_trackmove_trackdelete_tracksplitsplit_at_timestrim_cliproll_editslip_clipmove_clipnudge_clipsduplicate_clipsdelete_clipslift_rangeclose_gapscut_rangessplit_scenesgroup_clipsungroup_clipstransform_groupset_clip_enabledset_clip_labelrename_clipfreeze_framedetach_audiosync_to_clipcreate_compound_clipbreak_compound_clipedit_compoundrename_compoundbatchundoredoseek
Music, beats and markers 13
Picture and motion 25
set_paramadd_keyframemove_keyframemove_motion_pointset_keyframe_easeremove_keyframesframe_regionframe_clipsapply_layoutalign_clipsdistribute_clipsset_uniform_scaleset_flipset_cropset_maskset_blendset_chroma_keyset_canvas_fillset_background_removalremove_backgroundset_animationadd_cinematic_barstrack_motionauto_reframestabilize_clip
Graphics and compositing 11
Speed 6
Text and captions 11
Brand and ad elements 4
Effects, transitions and color 13
Sound 20
Check and deliver 10
The guide (help's overview)
Start here
Read before editing: the guide, the timeline and the names the tools accept.
help
The guide to these tools. topic: overview (the tools by step; start here), ad, epic_edit, voiceover or social_short (step-by-step playbooks), design (the rules of thumb every playbook follows, given once here), catalog, params (set_param paths), or any tool's name for its parameters and an example call.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
topic |
string | overview (default), design, ad, epic_edit, voiceover, social_short, catalog, params, or a tool's name |
Example:
{"name":"help","arguments":{"topic":"ad"}}
describe_project
Read the timeline: canvas, fps, duration, assets, tracks and clips with ids and times in seconds (a media clip's in/out and speed, keyframes, effects and look), markers and compound clips' nested timelines. Call this first and after edits. A long project's timeline runs to thousands of tokens, so ask for less: clip_ids (one clip's in-point and keyframes), track_ids or a stretch (start, end) narrow the clips (a stretch lists every track, those with nothing in it empty), and the media and markers listed follow them; clips false answers the tracks alone (ids, names, kinds, which is main, how many clips each holds) for a few hundred bytes; fields keeps only the clip fields named (fields ["text"]: captions as words and times); shown says how much of each list is in the answer. Example: {"clip_ids": ["c_12"]}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | Only these clips, each under its track (or its compound) | |
track_ids |
string[] | Only these tracks, each listed empty or not | |
start |
number | Only clips still playing after this timeline second (0..) | |
end |
number | Only clips starting before this timeline second; the same as start: what plays at that moment (0..) | |
assets |
boolean | false: no media list; true: all of it (default: all, or the clips' own when narrowed) | |
markers |
boolean | false: no markers; true: all (default: all, or those in the stretch or on the clips shown) | |
compounds |
boolean | false: no nested timelines (default: all, or those the clips shown play) | |
clips |
boolean | false: the tracks alone, each with its clip count instead of its clips (the cheap way to learn track ids); media, markers and nested timelines then only when asked true | |
brand |
boolean | The brand kit (set_brand): given with the whole timeline, left out of a narrowed answer unless true; false leaves it out of the whole one | |
fields |
string[] | Each clip's id, type, start and end, and only these of its fields, e.g. ["text"] for captions as words and times, ["transform", "keyframes"] for where things sit |
list_catalog
Every name the edit tools accept. With no section: an index of all sections (effects, transitions, animations, filters, text_templates, caption_looks, speed_presets, sound_effects, ambience, voices, blend_modes, mask_shapes, fonts, canvas_ratios, export_presets, sticker_motions, stickers, param_paths). With a section: its details, such as each effect's parameters with ranges and defaults, or each voice's accent, gender, grade, character and what to cast it for. section stickers with a query searches the sticker library.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
section |
string | A section name from the index | |
query |
string | Search words, for section stickers |
Example:
{"name":"list_catalog","arguments":{"section":"effects"}}
Projects
Work in a new project so the user's own stay untouched.
list_projects
The saved projects, most recently edited first, with ids for open_project. The one open in the editor is marked open: true; none is when the app shows its home screen.
No parameters.
open_project
Save the project in the editor and open another, by id or by name (any case; the most recently edited wins a tie). Answers a summary (name, canvas, fps, duration, counts, and each track's id, name, kind and clip count); describe_project has the timeline.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
project |
string | yes | Project id or name |
new_project
Save the project in the editor and start a new, empty one (1920x1080, 30 fps; set_project changes them). Work in one to leave the user's projects untouched. Answers a summary with its tracks' ids (the main track's among them); describe_project has the timeline.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
string | Project name; default: the date and time |
Example:
{"name":"new_project","arguments":{"name":"Spring sale ad"}}
save_project_copy
Save the open project as a copy under a new name, sharing its media, and open the copy (default) or stay. For a variant worked by hand, or a try that may be thrown away; the original stays as it was. For another shape of the same film, export_versions with save_projects saves it already laid out in that shape, to fine-tune.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
string | yes | The copy's name, e.g. 'Ember Roast 16x9' |
open |
boolean | Open the copy to edit it (default true) |
Example:
{"name":"save_project_copy","arguments":{"name":"Ember Roast 16x9","open":true}}
open_project_file
Open a project JSON as a new project identity. Desktop resolves relative media beside the file; missing media can be relinked. Omit path to use the picker. Answers a summary (name, canvas, fps, duration, counts); describe_project has the timeline.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
path |
string | Absolute desktop path to a project JSON file; omit for the native or browser picker |
collect_project
Copy the project JSON and its local media (proxies, LUTs and cutouts too) into a new folder named after the project (numbered when the name is taken), so it opens on another computer; nothing existing is overwritten. folder (an absolute path, created if missing) is where that new folder goes; without it the native dialog asks. Desktop only (the web downloads the JSON from File > Collect files). Answers the collected project.json's path.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
folder |
string | Absolute folder the collected project's new folder goes in, created if missing (desktop only); default: ask in the native dialog |
set_project
Change project settings: name, canvas size (pixels), frame rate, background color, master volume.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
string | Project name | |
width |
integer | Canvas width in pixels (16..8192) | |
height |
integer | Canvas height in pixels (16..8192) | |
background |
string | CSS hex color behind all layers | |
master_volume |
number | Master linear gain; 1 is unity (0..4) | |
fps |
"23.976" | "24" | "25" | "29.97" | "30" | "50" | "59.94" | "60" | Frame rate; clips keep their times |
Example:
{"name":"set_project","arguments":{"width":1080,"height":1920,"fps":"30"}}
Media
Bring files in and keep the library tidy.
import_media
Import media files into the project by absolute path (desktop) or URL. What a file is comes from the file (its path's or address's extension, else its contents), never from name, which is only the label the library shows. Answers one entry per file, in the order given: the new asset (id, name, kind, duration, width, height) for add_media_clip, a .cube LUT as {lut_id, name, kind: lut} for apply_lut, or {src, name, error} saying why that file failed; when every file fails, the call is an error naming each reason.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
files |
{ src, name }[] | yes | Files to import |
files[].src |
string | yes | Absolute path or URL |
files[].name |
string | The name the library shows (default the file's name); any text, it does not decide the format |
Example:
{"name":"import_media","arguments":{"files":[{"src":"C:/Footage/product.mp4","name":"Product hero"},{"src":"C:/Music/track.mp3"}]}}
remove_media
Remove imported media from the project; the files on disk stay. Media a clip uses is refused unless remove_clips=true, which deletes those clips too. unused=true removes every file no clip uses.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
asset_ids |
string[] | Assets | |
unused |
boolean | Remove all media no clip uses | |
remove_clips |
boolean | Also delete the clips that use it |
relink_media
Relink every use of an asset to another imported asset's source. Preserves edits and identity; requires compatible streams and duration. Undoable.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
asset_id |
string | yes | Original asset |
replacement_asset_id |
string | yes | Imported replacement asset |
replace_clip
Put another media file of the project in a clip's place (CapCut's Replace): the clip keeps its start, length (shorter only if the new source runs out), transform, effects, animations and sound settings, and plays the new source from its start.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video, image or audio clip |
asset_id |
string | yes | Media in the project |
rename_media
Rename an imported media file in the project's library; the file on disk keeps its name. Clips without a name of their own show the new name.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
asset_id |
string | yes | Media id from assets in describe_project |
name |
string | yes | The new name, 1 to 120 characters |
edit_media_folder
Create, rename or delete a virtual media folder. Deleting unfiles its media; source files and clips are preserved.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
action |
"create" | "rename" | "delete" | yes | Operation |
name |
string | yes | Folder name |
new_name |
string | New name for rename |
organize_media
Set virtual folder membership and/or favorite status for imported assets. An empty folder name unfiles media; does not move source files.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
asset_ids |
string[] | yes | Assets |
folder |
string | Existing folder, or empty to unfile | |
favorite |
boolean | Favorite status |
Timeline
Place, cut, move and arrange clips. batch runs many edits as one undo step.
add_media_clip
Place an imported asset (video, audio or image) on the timeline. Omit track_id to let the editor choose (main track if free, otherwise an overlay/audio track). Overwrite reports overwritten: [{id, kind, what: removed or trimmed}]. Use another track to preserve existing clips.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
asset_id |
string | yes | Asset id, from import_media or describe_project |
start |
number | yes | Timeline position in seconds (0..) |
duration |
number | Length in seconds; defaults to the whole source (one loop for animated images, 5 s for stills) (0.001..) | |
in |
number | Seconds into the source where the clip begins (0..) | |
track_id |
string | Track id | |
mode |
"overwrite" | "insert" | overwrite replaces what is under the clip; insert pushes later clips right |
Example:
{"name":"add_media_clip","arguments":{"asset_id":"a_123","start":0,"duration":3.5,"in":12}}
add_color_clip
Add a solid color layer (backgrounds, flashes, title cards). Overwrite reports overwritten: [{id, kind, what: removed or trimmed}]. Use another track to preserve existing clips.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
color |
string | yes | CSS color: '#ff3366', '#00000080' (with alpha, see-through), 'rgba(0,0,0,0.5)', 'white' |
start |
number | yes | Seconds (0..) |
duration |
number | yes | Seconds (0.001..) |
track_id |
string | Track id |
add_adjustment_clip
Add an adjustment layer: its color settings, filter and effects apply to every layer below it for its duration.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
start |
number | yes | Seconds (0..) |
duration |
number | yes | Seconds (0.001..) |
track_id |
string | Track id |
add_track
Add an empty video (visual) or audio track. Video tracks stack above existing ones, under a row of captions on top (captions stay the top layer; move_track above_track_id puts a track over them); audio tracks go at the bottom. role makes an audio track a stem: dialog, music, effects or ambience (measure_loudness, preview_audio and new sounds go by it).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
kind |
"video" | "audio" | yes | Track kind |
name |
string | Track name | |
role |
"dialog" | "music" | "effects" | "ambience" | Audio tracks: the stem it is |
update_track
Mute, solo, hide, lock, rename, resize, color, or set the volume, pan or role of a track. role (audio tracks: dialog, music, effects, ambience) says which stem it is; a track without one is judged by its files (a spoken line is dialog, a sound effect effects, an imported song music), never by its name.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
track_id |
string | yes | Track id |
name |
string | New name | |
muted |
boolean | Mute the track's sound | |
hidden |
boolean | Hide the track's picture | |
locked |
boolean | Prevent edits | |
volume |
number | Linear gain, 1 = unchanged (0..4) | |
pan |
number | -1 left .. 1 right, added to each clip's pan (-1..1) | |
solo |
boolean | While any track is soloed, only soloed tracks sound | |
color |
"none" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "purple" | "pink" | Track tint; a clip label takes precedence; none clears it | |
height |
"small" | "normal" | "large" | Row height on the timeline | |
role |
"dialog" | "music" | "effects" | "ambience" | "auto" | Audio tracks: the stem it is (auto clears it: judged by its files) |
move_track
Move a track up or down the stack, next to another track of its kind: above_track_id or below_track_id. tracks[0] in describe_project is the top. Higher pictures cover lower ones, and an adjustment clip grades every track below it, so a grade's track goes under the titles' and captions'. The main track stays the bottom video track.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
track_id |
string | yes | The track to move |
above_track_id |
string | Put it just above this track | |
below_track_id |
string | Put it just below this track |
Example:
{"name":"move_track","arguments":{"track_id":"t_grade","below_track_id":"t_captions"}}
delete_track
Remove a track and its clips. The main track stays.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
track_id |
string | yes | Track id |
split
Cut clips at a time. Without clip_ids, cuts every clip under that time on unlocked tracks.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
at |
number | yes | Seconds (0..) |
clip_ids |
string[] | Only these clips |
split_at_times
Split a video or audio clip at several SOURCE times (seconds into its media file, the way scene detection reports them). Times on or outside the clip's edges are ignored; a detached audio clip lined up with it splits too. For a cut at a timeline time use split.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
times |
number[] | yes | Source seconds |
trim_clip
Move a clip's start or end edge to a time. Limited by the source media and neighbors (the answer says where it stopped and why); ripple=true shifts later clips instead of stopping at them.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
edge |
"start" | "end" | yes | Which edge |
to |
number | yes | Seconds (0..) |
ripple |
boolean | Shift later clips |
roll_edit
Roll edit (the timeline's Roll tool): move the cut between a clip and the clip touching it to to (timeline seconds), the cut at its end by default (with the next clip) or at its start with edge start (with the clip before). One clip grows by what the other loses, so nothing else on the timeline moves: one call puts a cut on a beat, where two trim_clip calls had to come in the right order. Each clip keeps the pictures it shows at every other time; limited by both clips' source and by each keeping a frame (the answer says where it stopped and why). A cut with a gap at it has nothing to roll: trim_clip moves one edge.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | A clip on one side of the cut |
to |
number | yes | Timeline seconds the cut moves to (0..) |
edge |
"start" | "end" | Which of the clip's cuts: end (default, with the next clip) or start (with the clip before) |
Example:
{"name":"roll_edit","arguments":{"clip_id":"c_7","to":8}}
slip_clip
Slip a video or audio clip: play a later (by > 0) or earlier (by < 0) part of its source, keeping its place and length on the timeline, as when a shot's cut lands right but its moment is wrong. by is seconds of the source file, like in and cut_ranges, at any speed or ramp: in moves by exactly that much. Stops at the ends of the source, and says so.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip |
by |
number | yes | Seconds of source to move by (the in-point moves this much, whatever the clip's speed): positive shows later material |
Example:
{"name":"slip_clip","arguments":{"clip_id":"c_4","by":1.5}}
move_clip
Move a clip to a time and (optionally) another track of the same kind. Overwrites whatever is under its new position.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
start |
number | yes | Seconds (0..) |
track_id |
string | Destination track id |
nudge_clips
Move clips by whole frames on their tracks (negative is earlier). Nothing moves when a clip would pass the start, cover another clip or sits on a locked track.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips |
frames |
number | yes | Frames to move (-100000..100000) |
duplicate_clips
Copy clips with everything on them (effects, keyframes, animations, style), the earliest copy starting at at seconds (default: right after the last of them ends), each on its own clip's track or a new track above where that is taken. Answers with the copies. The way to repeat a styled title or a lower third. A compound clip's copy plays the same nested timeline (an edit inside shows in both) unless unique is true: then it gets its own copy of the timeline, and of every compound nested in it (answered under added_compounds), a template to change with edit_compound, e.g. replace_clip to put other footage in a styled window.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips to copy |
at |
number | Seconds where the earliest copy starts (0..) | |
unique |
boolean | Compound clips: copy their nested timelines too (and the compounds nested in them), so each copy can change on its own (default false: shared) |
Example:
{"name":"duplicate_clips","arguments":{"clip_ids":["c_window"],"at":6,"unique":true}}
delete_clips
Remove clips. ripple=true closes the gaps they leave on their tracks.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips to remove |
ripple |
boolean | Close the gaps |
lift_range
Take a timeline range out of every unlocked track (Premiere's Lift and Extract): clips crossing its edges are cut; ripple=false leaves a gap, ripple=true closes it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
start |
number | yes | Seconds (0..) |
end |
number | yes | Seconds (0..) |
ripple |
boolean | Close the gap (Extract) |
close_gaps
Close empty space on a track by rippling later clips left: the gap at at seconds, or every gap (packing the track from 0) when at is left out.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
track_id |
string | yes | Track id |
at |
number | Timeline seconds inside the gap |
cut_ranges
Remove parts of a video or audio clip. Ranges are SOURCE seconds (seconds into the clip's media file, the way silence detection and transcripts report them; describeProject lists each media clip's in/out), so one list stays valid however the clip is cut. ripple (default true) closes the gaps; a detached audio clip lined up with it is cut too. The clip keeps its id on the first piece left, its head cut or not; the other pieces get new ids. The primitive behind silence and filler removal.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
ranges |
{ start, end }[] | yes | Source ranges to remove; overlaps merge, parts outside the clip are ignored |
ranges[].start |
number | yes | Source seconds (0..) |
ranges[].end |
number | yes | Source seconds (0..) |
ripple |
boolean | Close the gaps (default true) |
split_scenes
Find every shot change in a video clip and split there (or drop markers). Returns the cut times in source seconds.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
sensitivity |
number | 0..1, default 0.5 (0..1) | |
output |
"split" | "markers" | Default split |
group_clips
Group two or more clips so they select, move, copy and delete together (Premiere's Group). They keep their tracks and settings.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips, two or more |
ungroup_clips
Break up the groups the given clips belong to.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips |
transform_group
Move, scale and rotate visual group members about their combined bounds in canvas pixels, preserving their arrangement. Audio members stay unchanged. Specify group_id or clip_ids. at creates keys for every member transform at timeline seconds; omitted, evaluate at the latest member start and update existing keyed paths there. Times outside a member clamp to its first or last frame. One undo step.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
group_id |
string | Existing group id; use this or clip_ids | |
clip_ids |
string[] | Explicit members; at least two visuals | |
x |
number | Horizontal offset as a fraction of canvas width; default 0 | |
y |
number | Vertical offset as a fraction of canvas height; default 0 | |
scale |
number | Uniform multiplier about the group center; default 1 (0.01..) | |
rotation |
number | Clockwise rotation offset in degrees; default 0 | |
at |
number | Timeline seconds to key every member; clamped to each member span (0..) |
set_clip_enabled
Turn clips off or on (Premiere's Enable): an off clip stays on the timeline, in place, but preview and export skip it (describe_project marks it disabled). A textless version's titles, or a clip an export_versions override hid, come back on with enabled true.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips |
enabled |
boolean | yes | false: off, skipped by preview and export; true: on |
set_clip_label
Color-label clips (Premiere's Label): the timeline paints them with it. "none" removes the label.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips to label |
label |
"red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "purple" | "pink" | "none" | yes | A label color, or none |
rename_clip
Name clips (Premiere's Rename): the timeline shows the name instead of the media's file name or a title's words. An empty name gives each clip its default name back. The media file keeps its name.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips to name |
name |
string | yes | The name, up to 120 characters; empty clears it |
freeze_frame
Hold the frame at a time inside a video clip as a still for duration seconds (default 3). mode insert (the default, CapCut's Freeze) splits the clip there and pushes the rest of its track right by the hold, the clips sitting on those shots (titles, captions, voice lines, effects) with them; at the clip's last frame the still takes that frame's place, so no one-frame piece is left after it. mode overwrite lays the still over what is there instead (the rest of the shot, a gap left on the main track for it, the next shot's head) and moves nothing: the freeze for a pause already cut to the music.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video clip id |
at |
number | yes | Timeline seconds |
duration |
number | Seconds to hold (0.04..) | |
mode |
"insert" | "overwrite" | insert pushes the rest of the track right; overwrite covers what is there and moves nothing |
Example:
{"name":"freeze_frame","arguments":{"clip_id":"c_7","at":6.267,"duration":1.733,"mode":"overwrite"}}
detach_audio
Move a video clip's sound to its own audio clip (for J/L cuts or separate audio edits).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video clip id |
sync_to_clip
Move a clip (a separately recorded sound) into sync with another (the camera clip of the same take), given where their sources line up: source second t of to_clip_id plays with source second t + offset of clip_id. Exact to the sample: the in-point takes up what a whole frame cannot; sound that would fall before 0 is trimmed off its head; a taken spot puts it on a new track. mute_reference sets to_clip_id's volume to 0. sync_audio finds the offset by waveform.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The clip to move: usually an audio clip |
to_clip_id |
string | yes | The clip it syncs to: usually the camera's video |
offset |
number | yes | Seconds: clip_id's source time minus to_clip_id's for the same moment |
mute_reference |
boolean | Silence to_clip_id's own sound (default false) |
create_compound_clip
Put clips into one compound clip (CapCut's Create compound clip, Premiere's Nest), on the lowest of their video tracks, spanning them all. The clips move into a nested timeline (describeProject lists it under compounds) at the same times relative to each other; the compound clip then plays it like a video clip plays a file, and its own transform, opacity, blend, color, effects, speed and volume act on the whole. Needs a clip on a video track, and nothing else on that track inside the span.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips to put inside, on any tracks |
name |
string | Name shown on the clip (default Compound clip N) |
break_compound_clip
Break a compound clip apart (Resolve's Decompose in place, CapCut's Undo compound clip): the clips inside go back on this timeline at the frames the compound clip showed them, trimmed to what it showed, on its track and the tracks above. The compound clip's own settings go with it. Needs speed 1.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Compound clip id |
edit_compound
Run another command inside a compound's nested timeline (compound_id from compounds in describeProject, not the compound clip's id). Clip and track ids, and times, are the nested timeline's own. Every compound clip that plays it shows the change.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
compound_id |
string | yes | Compound id |
command |
string | yes | Command to run inside, e.g. move_clip or set_param |
args |
object | That command's arguments |
rename_compound
Rename a compound; every compound clip that plays it shows the new name.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
compound_id |
string | yes | Compound id |
name |
string | yes | New name |
batch
Run several commands in order as one edit (one undo step), all or nothing: set_animation on many clips, a run of add_keyframe, a whole title sequence. Each step sees what the steps before it did; a failing step names its number and nothing changes. A step can name what an earlier step added: "$2" is the first clip step 2 added, "$2.2" its second, "$2.track" the track it added (steps count from 1), anywhere in args, e.g. [{command: 'add_text', args: {text: 'SALE', start: 1, duration: 2}}, {command: 'set_animation', args: {clip_id: '$1', slot: 'in', type: 'pop'}}]. Only editing commands batch; tools that run on this computer or look (add_sound_effect, add_voiceover, the AI tools, import_media, previews, checks, export) are called on their own.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
commands |
{ command, args }[] | yes | The steps, in order |
commands[].command |
string | yes | Command name, e.g. set_animation |
commands[].args |
object | That command's arguments | |
quiet |
boolean | Answer only the ids of the clips added, changed and removed, with any notes (every command a batch takes accepts it on its own too) |
Example:
{"name":"batch","arguments":{"commands":[{"command":"add_text","args":{"text":"SALE","start":1,"duration":2,"style":{"size":140,"bold":true}}},{"command":"set_animation","args":{"clip_id":"$1","slot":"in","type":"pop","duration":0.4}},{"command":"set_animation","args":{"clip_id":"$1","slot":"out","type":"fade","duration":0.3}}]}}
undo
Undo the last edit; returns what that changed back.
No parameters.
redo
Redo the last undone edit; returns what changed.
No parameters.
seek
Move the playhead to a time in seconds: what the viewer shows and where tools that act at the playhead (the Tracking box) start.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
at |
number | yes | Seconds (0..) |
Music, beats and markers
Compose an original score that fits the edit, or read an imported track's structure; cut and hit on it, fit it to the picture; mark moments.
generate_music
Compose and render an original, royalty-free score on this computer (no samples, nothing downloaded; about 10-15 s for a minute of music), imported as a WAV and placed on an audio track. The same arguments and seed give the same music. Styles: launch (Product launch: modern electronic, driving: four on the floor, pumping supersaw pad, sub bass, arp and a bright hook; 120 BPM); cinematic (Cinematic: big and swelling: string ostinato, legato strings and horns, choir, taiko and big hits, long hall reverb; 90 BPM); corporate (Upbeat corporate: bright and positive: piano eighths, plucked guitar strums, glockenspiel hook, claps, shaker and an eighth-note bass; 116 BPM); lofi (Lo-fi chill: laid back: swung boom-bap drums, Rhodes sevenths and ninths, round bass, sparse melody, vinyl crackle and tape wow; 80 BPM); ambient (Ambient: calm and spacious: evolving pads, glassy bells, a sub drone and a soft pulse, long hall reverb; 92 BPM). Give a length, or sections to fit the edit: [{name: "intro", bars: 4}, {name: "build", bars: 4}, {name: "drop", at: 8}, {name: "breakdown", bars: 4}, {name: "drop"}, {name: "outro", end_at: 56}]; names set what plays (intro, verse, build, drop/chorus/climax, breakdown/bridge, outro); times (at, end_at, seconds, length) land on bar lines by nudging the tempo (at most max_nudge), and fit says how close each came. Mastered to loudness (-14 LUFS) with the true peak under -1 dBTP. ending ring lets the final chord ring out over the last section (an outro: the final hit lands on its first beat, the end card's moment); stop ends on a short hit. stems also imports drums, bass, harmony and lead (they add up to the mix); place stems puts those on four grouped tracks instead of the mix. Answers the cue sheet in timeline seconds: bpm, key, chords, the beat and bar lengths with counts of each (detail full lists every bar and beat), the sections, the drops, final_hit, breaths (silences before drops), cut_points (drop, section, phrase, bar), fit, measured loudness and spectrum, the new asset and clip ids. markers adds analyze_music's markers. plan_music answers the same cue sheet without rendering: plan first, then generate with the seed it names. Placing and marking is one undo step (the files stay in the media); it cannot go in batch.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
style |
"launch" | "cinematic" | "corporate" | "lofi" | "ambient" | yes | launch (product launch: electronic, driving), cinematic (big, swelling), corporate (upbeat corporate), lofi (lo-fi chill), ambient |
bpm |
number | Tempo; default the style's (launch 120, cinematic 90, corporate 116, lofi 80, ambient 92) (40..220) | |
key |
string | The tonic, such as A, F# or Bb; Am or A minor also sets the mode (default the style's) | |
mode |
"major" | "minor" | "dorian" | "mixolydian" | "lydian" | "phrygian" | "harmonic_minor" | major, minor, dorian, mixolydian, lydian, phrygian or harmonic_minor (default the style's) | |
length |
number | Seconds in all, when no sections are given: the style's form is fitted to it (default 60, at most 300) (2..300) | |
sections |
{ name, bars, seconds, at, end_at, energy, seed }[] | The form, in order; any mix of bars, seconds and fixed times | |
sections[].name |
string | yes | intro, verse, build, drop (or chorus, climax), breakdown (or bridge), outro, or any name (its energy then decides) |
sections[].bars |
integer | Its length in bars of 4 beats (1..128) | |
sections[].seconds |
number | Its length in seconds instead, rounded to whole bars (0.5..) | |
sections[].at |
number | Seconds into the music where it must start (a drop at the edit's climax) (0..) | |
sections[].end_at |
number | Seconds into the music where it must end (the outro at the video's end) (0.5..) | |
sections[].energy |
number | How full and loud, 0..1 (default by name: drop 1, build 0.65, verse 0.5, breakdown 0.35, intro and outro 0.3) (0..1) | |
sections[].seed |
integer | Regenerate only this section's drums, fills and touches with another seed (0..) | |
seed |
integer | The same seed and arguments give the same music; default a new one, answered (0..) | |
ending |
"ring" | "stop" | ring (default): the final chord rings out over the last section; stop: a short final hit, then silence | |
fx |
boolean | Risers, impacts, reverse cymbals and braams into and on the drops (default true) | |
max_nudge |
number | How far the tempo may move to land asked-for times on bar lines, a fraction (default 0.06) (0..0.2) | |
start |
number | Timeline seconds the music starts at (default 0); the answer's times include it (0..) | |
detail |
"summary" | "full" | summary (default): the bars and beats as counts on the even grid (bar n at start + n x bar); full: every bar and beat listed | |
brightness |
number | The master's tilt about 1 kHz: 1 lifts the highs 6 dB from 3 kHz and lowers the lows 3 dB under 300 Hz, -1 the other way (darker, under a busy voice), loudness and true peak held; default the style's (launch 0.4, the rest 0) (-1..1) | |
loudness |
number | Integrated loudness to master to, LUFS (default -14, online video) (-30..-8) | |
stems |
boolean | Also import the drums, bass, harmony and lead stems (default false) | |
place |
"mix" | "stems" | "none" | mix (default): the mix on an audio track at start; stems: the four stems on four grouped tracks; none: import only | |
markers |
"beats" | "bars" | "phrases" | "sections" | "drop"[] | Markers to add, as analyze_music names them: beats (Beat), bars (Bar), phrases (Phrase), sections (each by name), drop (Drop) | |
name |
string | The media's name (default style, tempo, key and seed) |
Example:
{"name":"generate_music","arguments":{"style":"launch","key":"Am","sections":[{"name":"intro","bars":2},{"name":"build","bars":2},{"name":"drop","at":8},{"name":"breakdown","bars":4},{"name":"build","bars":2},{"name":"drop","bars":8},{"name":"outro","end_at":56}],"seed":7,"stems":true,"markers":["bars","drop"]}}
plan_music
The cue sheet generate_music would answer for these arguments, at once and without rendering or changing anything: the tempo after fitting, every section's start and end, the bar and beat grid (detail full lists every bar and beat), drops, final_hit, cut_points and how each asked-for time fit, plus the chord of every bar. Use it to try section lengths and times against the edit, then call generate_music with the same arguments and the seed it answers.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
style |
"launch" | "cinematic" | "corporate" | "lofi" | "ambient" | yes | launch (product launch: electronic, driving), cinematic (big, swelling), corporate (upbeat corporate), lofi (lo-fi chill), ambient |
bpm |
number | Tempo; default the style's (launch 120, cinematic 90, corporate 116, lofi 80, ambient 92) (40..220) | |
key |
string | The tonic, such as A, F# or Bb; Am or A minor also sets the mode (default the style's) | |
mode |
"major" | "minor" | "dorian" | "mixolydian" | "lydian" | "phrygian" | "harmonic_minor" | major, minor, dorian, mixolydian, lydian, phrygian or harmonic_minor (default the style's) | |
length |
number | Seconds in all, when no sections are given: the style's form is fitted to it (default 60, at most 300) (2..300) | |
sections |
{ name, bars, seconds, at, end_at, energy, seed }[] | The form, in order; any mix of bars, seconds and fixed times | |
sections[].name |
string | yes | intro, verse, build, drop (or chorus, climax), breakdown (or bridge), outro, or any name (its energy then decides) |
sections[].bars |
integer | Its length in bars of 4 beats (1..128) | |
sections[].seconds |
number | Its length in seconds instead, rounded to whole bars (0.5..) | |
sections[].at |
number | Seconds into the music where it must start (a drop at the edit's climax) (0..) | |
sections[].end_at |
number | Seconds into the music where it must end (the outro at the video's end) (0.5..) | |
sections[].energy |
number | How full and loud, 0..1 (default by name: drop 1, build 0.65, verse 0.5, breakdown 0.35, intro and outro 0.3) (0..1) | |
sections[].seed |
integer | Regenerate only this section's drums, fills and touches with another seed (0..) | |
seed |
integer | The same seed and arguments give the same music; default a new one, answered (0..) | |
ending |
"ring" | "stop" | ring (default): the final chord rings out over the last section; stop: a short final hit, then silence | |
fx |
boolean | Risers, impacts, reverse cymbals and braams into and on the drops (default true) | |
max_nudge |
number | How far the tempo may move to land asked-for times on bar lines, a fraction (default 0.06) (0..0.2) | |
start |
number | Timeline seconds the music starts at (default 0); the answer's times include it (0..) | |
detail |
"summary" | "full" | summary (default): the bars and beats as counts on the even grid (bar n at start + n x bar); full: every bar and beat listed | |
brightness |
number | The master's tilt about 1 kHz: 1 lifts the highs 6 dB from 3 kHz and lowers the lows 3 dB under 300 Hz, -1 the other way (darker, under a busy voice), loudness and true peak held; default the style's (launch 0.4, the rest 0) (-1..1) |
Example:
{"name":"plan_music","arguments":{"style":"cinematic","length":45,"ending":"stop"}}
analyze_music
Read a music clip's structure on this computer, as Final Cut's beat grid shows it (song parts, bars, beats), to cut to: bpm; grid, the beats as an even grid (first_beat, beat_seconds, bar_seconds, and off_grid: the beats more than 25 ms off it); downbeats (each bar's first beat, 4 beats a bar, from where the accents fall); phrases (every 4 bars); sections, each {start, end, label, energy} with label intro, verse, build, drop, breakdown or outro and energy 0..1 (1 the loudest part); drops (every qualifying rise after a dip) and drop (the biggest the clip plays, or null); counts of beats, bars, phrases and hits. detail full also lists every beat (beats), each beat's energy 0..1 (beat_energy) and hits (strong onsets on or off the beat: drum hits, stabs), about 2-3 KB a minute of music. Sections may be as short as two bars and their bounds follow bars or phrases. The whole music file is read (once; the reading is kept for every clip of it) and placed through the clip's in-point, speed and position, so a trimmed clip keeps the file's bar phase and drops; source says what was read. All times are TIMELINE seconds, inside the clip. confidence (0..1) says how sure the tempo, the bar lines and the sections are, with a note naming the hint to give when one is low. When the music's structure is known (a cue sheet, the composer's notes), give it in the file's own seconds: bpm, first_downbeat (a bar's first beat) and cues (each part's start and label) replace what would be detected. markers also drops markers: beats (label Beat), bars (Bar), phrases (Phrase), sections (each by name), drop (a Drop marker for every drop), one undo step; they follow the clip through moves, trims, splits and speed changes (a second the clip no longer plays loses its marker), and a second reading replaces the clip's own markers of those kinds instead of adding to them; cut_to_beats then cuts on them (grid bars or phrases), add_beat_hits hits them (marker_label Bar or Drop) and fit_music moves the drop. bpm is the tempo as the clip plays it (file_bpm, the file's own, when it is sped up). When the reading is wrong for good (bars between the kicks, a drop missed), set_music_grid keeps the right grid with the file, and every later reading uses it. Example: {"clip_id": "c_3", "markers": ["bars", "drop"]}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The music clip (audio, or a video with sound) |
markers |
"beats" | "bars" | "phrases" | "sections" | "drop"[] | Kinds of markers to add (default none) | |
bpm |
number | The tempo, when known: beats are tracked at it (40..240) | |
first_downbeat |
number | A bar's first beat, in the music file's own seconds (0 for a score that starts on a bar): sets where bars start; with bpm, the whole grid (0..) | |
cues |
{ at, label }[] | The parts, when known, in the music file's own seconds: each starts at its at and runs to the next; a drop cue after another part is a drop. [{at: 0, label: 'intro'}, {at: 4, label: 'build'}, {at: 8, label: 'drop'}, {at: 32, label: 'breakdown'}, {at: 36, label: 'drop'}, {at: 48, label: 'outro'}] | |
cues[].at |
number | yes | Source seconds the part starts (0..) |
cues[].label |
"intro" | "verse" | "build" | "drop" | "breakdown" | "outro" | yes | What the part is |
detail |
"summary" | "full" | summary (default): the grid, bars, phrases, parts and drops with counts; full: also every beat, hit and beat's energy |
Example:
{"name":"analyze_music","arguments":{"clip_id":"c_3","markers":["beats","bars","drop"]}}
set_music_grid
Give a song's grid yourself, when analyze_music reads it wrong (its confidence is low, the bars fall between the kicks, the drop is missed or early) or when it is known (a cue sheet, the composer's notes): the tempo, a bar's first beat and, if known, its parts (a drop after another part is a drop), all in the music file's own seconds, four beats a bar. The grid is kept with the file: analyze_music, mark_beats and check_project read the song through it instead of guessing, on every clip of it, through trims, moves and speed changes. markers places the grid's markers on this clip now (Beat, Bar, Phrase, each part by name, Drop), following the clip as analyze_music's do and replacing the clip's own earlier ones of those kinds; by default the kinds it is marked with already (parts and the drop only when sections are given). The answer's note says where the grid plays on the timeline. clear: true forgets the grid. Example: {clip_id: 'c_3', bpm: 128, first_downbeat: 0, sections: [{at: 0, label: 'intro'}, {at: 7.5, label: 'build'}, {at: 15, label: 'drop'}, {at: 33.75, label: 'outro'}], markers: ['beats', 'bars', 'drop']}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The music clip (audio, or a video with sound); the grid is kept with its file |
bpm |
number | Beats a minute, in the file's own time (a clip sped up plays it faster) (40..240) | |
first_downbeat |
number | A bar's first beat, in the music file's own seconds (0 for a song that starts on a bar) (0..) | |
sections |
{ at, label }[] | The parts, if known, in the music file's own seconds: each starts at its at and runs to the next | |
sections[].at |
number | yes | File seconds the part starts (0..) |
sections[].label |
"intro" | "verse" | "build" | "drop" | "breakdown" | "outro" | yes | What the part is |
markers |
"beats" | "bars" | "phrases" | "sections" | "drop"[] | Markers to place on this clip from the grid: beats, bars, phrases, sections, drop (default: the kinds the clip is marked with already; [] none) | |
clear |
boolean | Forget the grid: analysis guesses again |
Example:
{"name":"set_music_grid","arguments":{"clip_id":"c_3","bpm":128,"first_downbeat":0.12,"sections":[{"at":0,"label":"intro"},{"at":7.62,"label":"build"},{"at":15.12,"label":"drop"}],"markers":["beats","bars","drop"]}}
cut_music
Cut bars out of a song and join it without a click: from and to are the music file's seconds (as cut_ranges takes them), each moved to the song's nearest bar line (snap beat: beat; none: as given) from its grid (set_music_grid; analyze_music reads bpm and the first downbeat to give it), the stretch removed and the gap closed. The piece after plays on exactly whole bars later in the file, so the beat runs on, and the two pieces meet with a short equal-power crossfade that ends on the downbeat (crossfade, default 0.03 s; 0 for a butt cut, which plays a few milliseconds of near-silence at the join). The answer's note says the file seconds cut and where the pieces meet; preview_audio's edges then measure the join. repeat n plays the bars from-to n more times right after they play instead of cutting them (a loop: the song grows by whole bars, its later part and every clip after it on its track playing that much later, each jump back joined as a cut's is): to land a final hit later, or to fill a longer cut. Example: {"clip_id": "c_3", "from": 14, "to": 30}; four bars at 120 BPM played twice: {"clip_id": "c_3", "from": 8, "to": 16, "repeat": 1}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The song's audio clip |
from |
number | yes | File seconds to cut from (moved to the nearest bar line) (0..) |
to |
number | yes | File seconds to cut to (moved to the nearest bar line) (0..) |
snap |
"bar" | "beat" | "none" | Where from and to land: bar (default), beat, or none (as given) | |
crossfade |
number | Seconds the pieces crossfade over, ending at the cut (default 0.03; 0 a butt cut) (0..2) | |
repeat |
number | Play from-to this many more times right after it instead of cutting it (1 plays those bars twice), 1 to 16 (1..16) |
Example:
{"name":"cut_music","arguments":{"clip_id":"c_3","from":14,"to":30}}
cut_to_beats
Cut a montage to the music in one call: lay shots end to end on the main track so every cut lands on the beat grid. The grid is the Beat markers (mark_beats or analyze_music), Bar or Phrase markers (grid bars or phrases, from analyze_music with markers ["bars"] or ["phrases"]), or beats: your own timeline seconds. Each shot lasts every grid steps (default 2 beats, or 1 bar or phrase), or the steps in pattern in turn ([4, 4, 2, 2, 1, 1] speeds up), or its own beats. The first shot starts at start; when no grid point is there, the first one after it counts as beat one, so an intro stays inside the first shot. A shot reads its media from in (default: where the last shot of the same file ended, so one long clip gives many different shots), slid back inside the file if it would run past the end; media too short for its slot ends on an earlier grid point. Stops when the shots or the grid run out, or at end; clips already there are overwritten. Video shots are muted (mute false keeps their sound). Example: {"media": [{"asset_id": "a_1"}, {"asset_id": "a_2", "in": 3}, {"asset_id": "a_1", "beats": 4}], "pattern": [2, 2, 1, 1]}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
media |
{ asset_id, in, beats }[] | yes | The shots in order; the same asset may appear many times |
media[].asset_id |
string | yes | Video or picture asset id (import_media) |
media[].in |
number | Source seconds the shot starts at (default: where this media's previous shot ended, else 0) (0..) | |
media[].beats |
integer | Grid steps this shot lasts, overriding pattern and every (1..) | |
grid |
"beats" | "bars" | "downbeats" | "phrases" | Which markers make the grid: beats (Beat, default), bars or downbeats (Bar: each bar's first beat), phrases (Phrase: every 4 bars) | |
beats |
number[] | The grid as timeline seconds instead of markers, e.g. analyze_music's downbeats | |
marker_label |
string | Use the markers with this label as the grid instead | |
every |
integer | Grid steps per shot (default 2 for beats, 1 for bars or phrases) (1..64) | |
pattern |
integer[] | Grid steps per shot, cycling: [4, 4, 2, 2, 1, 1] | |
start |
number | Timeline seconds the first shot starts (default 0) (0..) | |
end |
number | Timeline seconds to stop by: no cut after it (0..) | |
track_id |
string | A video track instead of the main track | |
mute |
boolean | Mute the shots' own sound (default true: the music carries a montage) |
Example:
{"name":"cut_to_beats","arguments":{"media":[{"asset_id":"a_1","in":2},{"asset_id":"a_2"},{"asset_id":"a_1","in":9,"beats":4}],"grid":"beats","pattern":[4,4,2,2,1,1]}}
add_beat_hits
Punch up beats with short hits: flash (blown out to white), zoom (a zoom punch), shake (camera shake), rgbSplit, glow or zoomBlur, several at once as one layer (["flash", "zoom"]). Each hit is an adjustment layer on a Hits track above the footage (below bars and titles), at its peak on the beat and easing out over duration (default 0.2 to 0.35 s by effect), keyframed, so preview and export match. Times: times (timeline seconds), the main track's cuts (at_cuts true), or the Beat markers (or marker_label's, e.g. Bar or Drop), between from and to, thinned to every Nth. intensity 0..1 (default 1, strong). Photosensitivity: flashes keep to at most 3 in any second (WCAG 2.3.1); a beat that would break that gets its other effects without the flash, or nothing. Example: {"effects": ["zoom", "rgb_split"], "marker_label": "Bar"}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
effect |
"flash" | "zoom" | "shake" | "rgbSplit" | "glow" | "zoomBlur" | One hit | |
effects |
"flash" | "zoom" | "shake" | "rgbSplit" | "glow" | "zoomBlur"[] | Several hits on each beat, as one layer | |
times |
number[] | Timeline seconds of the hits | |
at_cuts |
boolean | Hit every cut of the main track instead | |
marker_label |
string | Hit the markers with this label (default Beat) | |
every |
integer | Only every Nth time, from the first (default 1) (1..64) | |
from |
number | Timeline seconds: no hit before (0..) | |
to |
number | Timeline seconds: no hit at or after (0..) | |
duration |
number | Seconds each hit lasts (0.04..2) | |
intensity |
number | 0..1, default 1 (0..1) |
Example:
{"name":"add_beat_hits","arguments":{"effects":["flash","zoom"],"marker_label":"Drop"}}
fit_music
Fit a music clip to the cut. With drop_at, first slide the music so its drop plays at drop_at (timeline seconds): drop is where the drop plays now (analyze_music's drop; default the Drop marker on the clip), anything that would fall before 0 is cut off, and its Beat, Bar, Phrase and section markers move with it. Then end it with the picture (end video, default), on the last Beat or Bar marker before the picture ends (end beat or bar), or at end_at, fading out over fade_out seconds (default 1). trim_picture cuts the picture there too, so both stop together. Align the drop before cut_to_beats: the cuts follow the markers. Example: {"clip_id": "c_2", "drop_at": 4.0, "end": "bar", "fade_out": 1.5}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The music clip (audio, or a video's sound) |
drop_at |
number | Timeline seconds the drop should land on (0..) | |
drop |
number | Timeline seconds where the drop plays now (default the Drop marker on the clip) (0..) | |
end |
"video" | "beat" | "bar" | Where the music ends: video (the picture's end, default), beat or bar (the last Beat or Bar marker before it) | |
end_at |
number | Timeline seconds to end at instead (0..) | |
fade_out |
number | Seconds of fade-out (default 1) (0..30) | |
fade_in |
number | Seconds of fade-in (default unchanged) (0..30) | |
trim_picture |
boolean | Also cut the picture (every video track) at the music's end |
Example:
{"name":"fit_music","arguments":{"clip_id":"c_3","drop_at":8.4,"end":"bar","fade_out":1.5}}
mark_beats
Mark a music clip's beats (every Nth) with Beat markers, from the whole song's reading (analyze_music's beats, read through the grid set_music_grid gave the file), placed where the clip plays them: trimmed, moved or sped up, the markers stand on the song's beats, and they follow the clip through later edits, replacing its earlier Beat markers. Answers bpm as the clip plays it (file_bpm, the file's own, when it is sped up or slowed), the beats marked in timeline seconds and how many markers it added (0 when they were there already: a second call replaces them, never doubles them).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
every |
1 | 2 | 4 | Marker on every Nth beat (default 1) |
Example:
{"name":"mark_beats","arguments":{"clip_id":"c_123","every":1}}
add_marker
Drop a marker at a time (chapters, beats, notes).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
at |
number | yes | Seconds (0..) |
label |
string | Text | |
color |
string | CSS color |
add_markers
Drop several markers at once at TIMELINE seconds (beats, scene cuts, chapters). A time that already has a marker with the same label is skipped, so running a detector twice does not double its markers.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
times |
number[] | yes | Timeline seconds |
label |
string | Text for every marker | |
color |
string | CSS color |
update_marker
Edit a timeline marker's position, label or color without moving clips. Times are seconds. A marker that follows a clip (describe_project names its clip) stops following it when moved; detach: true stops it where it stands.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
marker_id |
string | yes | Marker id |
at |
number | Timeline seconds (0..) | |
label |
string | Text | |
color |
string | CSS color | |
detach |
boolean | Stop following its clip, staying where it is |
delete_markers
Delete the specified timeline markers together. Unknown ids reject the whole edit.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
marker_ids |
string[] | yes | Marker ids |
Picture and motion
Position, scale, crop, mask, key and animate pictures; keyframes for anything numeric.
set_param
Set a numeric clip parameter by path, e.g. opacity (0..1), volume (1 = 0 dB), transform.x / transform.y (canvas fractions from center), transform.scale (1 = fit; the horizontal scale when not uniform), transform.scaleY (vertical scale; setting it makes the scale non-uniform), transform.rotation (degrees), adjust.exposure / adjust.contrast / adjust.saturation / adjust.temperature (-100..100), grade values such as adjust.hsl.red.saturation, adjust.wheels.gain.y (-100..100) and adjust.lut.intensity (0..1). If the path has keyframes, sets a keyframe at at instead (without at it is refused: remove_keyframes first to set one value), which ramps from the key before; hold true keeps what plays before at as it was (a key on the frame before holds it) and changes the value there, as a step (on a path without keys it makes those two keys). An effect's parameter: effects.. (its place in the stack, from 0), effects.. or effects.., e.g. effects.0.amount; list_catalog section effects has each one's parameters. Sound fades are frames here; set_fades takes seconds.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Clips |
path |
string | yes | Parameter path |
value |
number | yes | New value |
at |
number | Timeline seconds, for keyframed paths | |
hold |
boolean | With at: hold the value before at until the frame before it, so the change is a step there and not a ramp from the key before |
Example:
{"name":"set_param","arguments":{"clip_ids":["c_123"],"path":"transform.scale","value":1.2}}
add_keyframe
Animate a parameter: add a keyframe at a timeline time. Two or more keyframes on a path interpolate between them, each shaped toward the next by its ease. Only paths that animate on the clip are accepted (help topic params lists them); any other is refused with the list.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
path |
string | yes | Parameter path that animates on this clip, e.g. transform.x, opacity, effects.0.amount (help topic params) |
at |
number | yes | Timeline seconds |
value |
number | Value at that time | |
color |
string | For a color path (style.color): the CSS color at that time, instead of value | |
ease |
"linear" | "hold" | "easeIn" | "easeOut" | "easeInOut" | "smooth" | "fastIn" | "overshoot" | "anticipate" | "spring" | "elastic" | "bounce" | Curve toward the next keyframe: linear, hold, easeIn, easeOut, easeInOut, smooth, fastIn, overshoot, anticipate, spring, elastic, bounce. fastIn is the modern expo-out (arrives fast, glides to rest); spring, elastic and bounce overshoot and settle | |
bezier |
number[] | [x1, y1, x2, y2], a CSS cubic-bezier (x1 and x2 in 0..1, y may overshoot); overrides ease |
Example:
{"name":"add_keyframe","arguments":{"clip_id":"c_123","path":"transform.x","at":1,"value":0.75,"ease":"fastIn"}}
move_keyframe
Retime a keyframe and/or change its value. at is the keyframe's current timeline time, to the new one (seconds). It stays inside the clip and replaces a keyframe already at its new time on that path.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
path |
string | yes | Parameter path (see set_param) |
at |
number | yes | Timeline seconds of the keyframe now |
to |
number | Timeline seconds to move it to | |
value |
number | New value |
move_motion_point
Move an existing position-key point in one undo step. Sets x and y keys at its time, preserving ease and other keys. Use add_keyframe to create a new time first.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Visual clip id |
at |
number | yes | Timeline seconds of an existing x or y key (0..) |
x |
number | yes | Position as a fraction of canvas width from center |
y |
number | yes | Position as a fraction of canvas height from center |
set_keyframe_ease
Shape the curve leaving keyframes toward the next one. ease is a preset (linear, hold, easeIn, easeOut, easeInOut, smooth, fastIn, overshoot, anticipate, spring, elastic, bounce); bezier [x1, y1, x2, y2] is a CSS cubic-bezier (x in 0..1, y may overshoot) and overrides it. Without path, applies to every parameter keyed at that time; without at, to every keyframe on the path(s).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
path |
string | Parameter path; omit for all keyed parameters | |
at |
number | Timeline seconds of the keyframe; omit for all keyframes | |
ease |
"linear" | "hold" | "easeIn" | "easeOut" | "easeInOut" | "smooth" | "fastIn" | "overshoot" | "anticipate" | "spring" | "elastic" | "bounce" | Curve toward the next keyframe: linear, hold, easeIn, easeOut, easeInOut, smooth, fastIn, overshoot, anticipate, spring, elastic, bounce. fastIn is the modern expo-out (arrives fast, glides to rest); spring, elastic and bounce overshoot and settle | |
bezier |
number[] | [x1, y1, x2, y2], a CSS cubic-bezier (x1 and x2 in 0..1, y may overshoot); overrides ease |
remove_keyframes
Remove keyframes from clip_ids (or clip_id). at lists timeline seconds (omit for every time); path limits it to one parameter (omit for all). A parameter left with no keys returns to its static value. No matching keys is a reported no-op, including in a batch.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | Clip id; alternatively use clip_ids | |
clip_ids |
string[] | Clips whose matching keys are removed | |
path |
string | Parameter path; omit for all keyed parameters | |
at |
number[] | Timeline seconds of the keyframes to remove |
Example:
{"name":"remove_keyframes","arguments":{"clip_ids":["c_title"],"path":"opacity"}}
frame_region
Show one part of a video or picture full screen, as a zoom on a screen recording does: region is a rectangle in fractions of the source picture (x, y its top-left corner; {x:0.5,y:0,w:0.5,h:1} is the right half). fill (default) covers the canvas, fit shows all of the region. The picture slides back where it would leave an empty band, which keeps a part at the picture's edge at the canvas's edge; place_at {x, y} instead puts the region's center at that point of the canvas (fractions from its center, as positions are: {x: 0, y: 0} centers it at any zoom, {x: 0, y: -0.15} above the middle, clear of a tall frame's buttons), leaving a band where the picture runs out. With to_region, it moves from the one to the other between from and to (default the whole clip), easing in and out. moves, instead of region, is any number of moves in one call: each region is shown at its at (timeline seconds), the picture moving there from the move before (ease, default easeInOut); before the first it holds the first, and a hold is the same region again, so a push-in and a later pan is [{region: whole, at: 12.9}, {region: A, at: 13.7}, {region: A, at: 15.4}, {region: B, at: 15.8}]. Replaces the clip's scale and position keyframes; titles, shapes and callouts pinned to the clip (anchor, add_callout) follow.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or image clip id |
region |
{ x, y, w, h } | The part of the picture to show (or to start from when to_region is given): fractions, x and y its top-left corner | |
region.x |
number | yes | Left edge, a fraction of the picture's width (0..1) |
region.y |
number | yes | Top edge, a fraction of its height (0..1) |
region.w |
number | yes | Width, a fraction of the picture's width (0.01..1) |
region.h |
number | yes | Height, a fraction of its height (0.01..1) |
to_region |
{ x, y, w, h } | Where the move ends: the part of the picture shown at to |
|
to_region.x |
number | yes | Left edge, a fraction of the picture's width (0..1) |
to_region.y |
number | yes | Top edge, a fraction of its height (0..1) |
to_region.w |
number | yes | Width, a fraction of the picture's width (0.01..1) |
to_region.h |
number | yes | Height, a fraction of its height (0.01..1) |
mode |
"fill" | "fit" | How the region meets the canvas | |
from |
number | Timeline seconds the move starts; default the clip's start | |
to |
number | Timeline seconds the move ends; default the clip's end | |
moves |
{ region, at, ease, place_at }[] | Instead of region, to_region, from and to: the regions in order, each shown at its at, moving there from the one before | |
moves[].region |
{ x, y, w, h } | yes | The part shown at at |
moves[].region.x |
number | yes | Left edge, a fraction of the picture's width (0..1) |
moves[].region.y |
number | yes | Top edge, a fraction of its height (0..1) |
moves[].region.w |
number | yes | Width, a fraction of the picture's width (0.01..1) |
moves[].region.h |
number | yes | Height, a fraction of its height (0.01..1) |
moves[].at |
number | yes | Timeline seconds the region fills the frame (0..) |
moves[].ease |
"linear" | "hold" | "easeIn" | "easeOut" | "easeInOut" | "smooth" | "fastIn" | "overshoot" | "anticipate" | "spring" | "elastic" | "bounce" | How the move into this region goes: linear, hold, easeIn, easeOut, easeInOut, smooth, fastIn, overshoot, anticipate, spring, elastic, bounce; default easeInOut | |
moves[].place_at |
{ x, y } | This move's place_at, instead of the call's | |
moves[].place_at.x |
number | Across the canvas from its center: -0.5 left edge .. 0.5 right edge; default 0 (-0.5..0.5) | |
moves[].place_at.y |
number | Down the canvas from its center: -0.5 top .. 0.5 bottom; default 0 (-0.5..0.5) | |
place_at |
{ x, y } | Put the region's center at this point of the canvas (fractions from its center) at any zoom, instead of sliding the picture back from its edges; a band shows where the picture runs out | |
place_at.x |
number | Across the canvas from its center: -0.5 left edge .. 0.5 right edge; default 0 (-0.5..0.5) | |
place_at.y |
number | Down the canvas from its center: -0.5 top .. 0.5 bottom; default 0 (-0.5..0.5) |
Example:
{"name":"frame_region","arguments":{"clip_id":"c_123","region":{"x":0.25,"y":0.2,"w":0.5,"h":0.5},"to_region":{"x":0.1,"y":0.1,"w":0.8,"h":0.8},"from":0,"to":3}}
frame_clips
Frame pictures on the canvas, centered: fit shows the whole picture, fill covers the canvas and crops the overflow, stretch covers it exactly, ignoring proportions.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
mode |
"fit" | "fill" | "stretch" | yes | How to frame |
at |
number | Timeline seconds, for animated scale |
apply_layout
Arrange 2 to 4 overlapping visual clips in input order. fit cover (default) fills each cell, cropping the picture; contain shows the whole picture centered in its cell (screen recordings, slides), with background filling the cell behind it. Triptych needs 3. PIP keeps the first clip full frame and stacks the remaining cards above it. The clips move to Layout tracks directly above the highest of their tracks, so titles already above them stay on top. Resets transform, crop and mask keys and clip animations to fit the cells. Optional borders and backgrounds are editable shape layers grouped with their pictures. One undo step.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | 2 to 4 distinct visual clip ids in cell order; PIP main first |
layout |
"split-vertical" | "split-horizontal" | "grid-2x2" | "triptych" | "pip-top-right" | "pip-top-left" | "pip-bottom-right" | "pip-bottom-left" | "pip-center-card" | yes | Cell arrangement |
gap |
number | Gutter as fraction of the shorter canvas side; default 0.02 (0..0.3) | |
margin |
number | Outer margin as fraction of the shorter canvas side; default 0.04 (0..0.4) | |
radius |
number | Rounded mask radius, 0 square to 1 half the shorter cell side; default 0 (0..1) | |
border |
{ width, color } | Optional editable border shapes | |
border.width |
number | yes | Stroke width in pixels at 1080p (0..100) |
border.color |
string | yes | CSS border color |
animate |
"none" | "slide" | "scale" | Entrance over 0.4 seconds, clipped to clip duration; default none | |
fit |
"cover" | "contain" | cover (default): fill each cell, cropping the picture; contain: the whole picture, centered in its cell (the PIP's full-frame picture always covers) | |
background |
string | With fit contain: a CSS color filling each cell behind its picture, as an editable shape |
align_clips
Align pictures on the canvas, as CapCut's Transform align row does: one clip against the canvas edge or center, several against their combined bounds. left, center and right move horizontally; top, middle and bottom vertically. Animated positions get a keyframe at at (default: each clip's first frame).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
align |
"left" | "center" | "right" | "top" | "middle" | "bottom" | yes | Where to align |
at |
number | Timeline seconds, for animated positions | |
margin |
number | One clip: stop this far inside the canvas edge, a fraction of its size (0.05 is the title-safe inset), so titles of any width share one left edge (0..0.4) |
distribute_clips
Space three or more pictures evenly on the canvas: the outermost two stay, the rest move so the gaps between neighbors are equal.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips, three or more |
axis |
"horizontal" | "vertical" | yes | Direction |
at |
number | Timeline seconds, for animated positions |
set_uniform_scale
CapCut's Uniform scale switch. false: the vertical scale (transform.scaleY) separates from transform.scale, starting equal; true: it goes and the picture takes transform.scale.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
uniform |
boolean | yes | Scale both directions together |
set_flip
Mirror pictures: horizontal flips left-right, vertical flips upside down. A direction left out keeps its state.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
horizontal |
boolean | Flipped left-right | |
vertical |
boolean | Flipped upside down |
set_crop
Crop pictures, as CapCut's Crop does. left, top, right and bottom are fractions of the source trimmed from each edge (0 keeps the edge; each pair must leave at least 5% of the picture); edges left out keep their value. Or give ratio instead for the largest centered crop of that shape on each clip's source (original: uncropped). The crop is cut from the source before flips, scale and rotation, and the part kept stays where it was.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
left |
number | Fraction trimmed from the source's left (0..0.95) | |
top |
number | Fraction trimmed from the top (0..0.95) | |
right |
number | Fraction trimmed from the right (0..0.95) | |
bottom |
number | Fraction trimmed from the bottom (0..0.95) | |
ratio |
"original" | "16:9" | "9:16" | "1:1" | "4:3" | "3:4" | "4:5" | "21:9" | Crop to this shape, centered |
set_mask
CapCut's mask on pictures: shape linear (keeps above a line), mirror (a band), circle, rectangle, heart or star; none removes it. x and y move its center from the picture's center, width and height size it, all as fractions of the visible picture's width and height (so on a 16:9 picture width 0.44 with height 0.44 is an oval: aspect 1 makes a round circle or a square, its height following its width, or its width a height given alone); rotation in degrees turns it; feather 0..1 softens the edge; radius 0..1 rounds a rectangle's corners; invert keeps the outside. A new mask starts centered at 0.6 of the picture (hearts and stars square). A circle, rectangle, heart or star reaching past the picture's edge is cut flat there: the answer's notes name the edge and the x, y or size that keeps it whole. Values left out keep theirs. With at (timeline seconds), the values given are keyed there: one with no keys yet first gets a key at the clip's start holding what it was, so two calls (the same values at the moment it leaves, the new ones where it lands) move the mask; on a mask this call makes, at is not used (the answer says so). A spotlight that follows a screen recording's zooms is add_callout.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
shape |
"none" | "linear" | "mirror" | "circle" | "rectangle" | "heart" | "star" | Mask shape | |
x |
number | Center offset, fraction of the picture's width (-1..1) | |
y |
number | Center offset, fraction of the picture's height (-1..1) | |
width |
number | Fraction of the picture's width (0.01..2) | |
height |
number | Fraction of the picture's height (0.01..2) | |
rotation |
number | Degrees, clockwise (-360..360) | |
feather |
number | Edge softness (0..1) | |
radius |
number | Rectangle corner roundness (0..1) | |
invert |
boolean | Keep the outside instead | |
aspect |
number | The mask's width over its height in pixels (1: a round circle, a square); its height follows from width, or its width from a height given alone (0.05..20) | |
at |
number | Timeline seconds, for animated values |
set_blend
How a picture mixes with what is under it (the inspector's Blend). screen or add for light leaks, flares and glows shot on black; multiply for textures and paper on white; overlay or softLight for grain and grunge. normal undoes it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
mode |
"normal" | "darken" | "multiply" | "colorBurn" | "lighten" | "screen" | "colorDodge" | "add" | "overlay" | "softLight" | "hardLight" | "difference" | "exclusion" | yes | Blend mode |
Example:
{"name":"set_blend","arguments":{"clip_ids":["c_123"],"mode":"screen"}}
set_chroma_key
Key out a color (green or blue screen) so what is below shows through, as CapCut's Chroma key. color is the backdrop's color (#00b140 is studio green); similarity 0..1 (default 0.4) is how close a pixel must be to vanish, smoothness 0..1 (default 0.1) softens edges, spill 0..1 (default 0.5) takes the green cast off the subject. enabled=false turns it off and keeps the settings.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or image clips |
color |
string | Key color, CSS hex | |
similarity |
number | 0..1 (0..1) | |
smoothness |
number | 0..1 (0..1) | |
spill |
number | 0..1 (0..1) | |
enabled |
boolean | Keying on (default true) |
set_canvas_fill
What fills the canvas around a picture that does not cover it (CapCut's Canvas): blur (the clip itself, scaled to cover and blurred; amount 0..100, default 50), color (a solid color), or none. The usual fix for horizontal footage on a vertical canvas without cropping it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or image clips |
fill |
"blur" | "color" | "none" | yes | Kind of fill |
amount |
number | Blur strength 0..100 (0..100) | |
color |
string | Fill color, CSS hex |
Example:
{"name":"set_canvas_fill","arguments":{"clip_ids":["c_123"],"fill":"blur","amount":60}}
set_background_removal
Turn Remove background on or off for video clips that already have a background matte (the person stays, everything behind them becomes see-through, so lower tracks show). The remove_background tool (window.videoEditor.ai.removeBackground) makes the matte on this machine; turning it off keeps the matte, so turning it back on is instant.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video clips |
enabled |
boolean | yes | On or off |
remove_background
Remove the background of a video clip of a person or object (CapCut's Remove BG): a segmentation model on this machine makes a matte for every frame (seconds to minutes; the first run downloads the model), then the subject plays over whatever is on the tracks below. One undoable edit; set_background_removal turns it off and on again instantly.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video clip id |
set_animation
Set a clip's intro (in), outro (out) or looping (loop) animation; type 'none' clears the slot. Whole clip, in/out: fade, zoomIn, zoomOut, slideLeft, slideRight, slideUp, slideDown, rise, drop, pop, spin, tilt, blurIn; loop: pulse, sway, float, shake, flicker, heartbeat. Titles only (kinetic typography), in/out: typewriter (letters appear one by one behind a caret; as out it deletes from the end), letterRise, letterFade, wordPop, wordRise, wordSlide, lineRise (each letter, word or line moves on its own), highlightSweep (a marker box sweeps behind the words) and underlineWipe (an underline draws across them), both in the title's style.accent color. On a title, unit plays any in, out or loop animation per letter, word or line instead (rise by word; a float loop by letter is a wave); stagger spreads the units over the duration and order picks which goes first. duration is the whole animation in seconds, every unit included (a typewriter over 1.5 s suits a 25-letter line); for a loop, one cycle. Example: {clip_id, slot: 'in', type: 'rise', unit: 'word', stagger: 0.6, duration: 0.8}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
slot |
"in" | "out" | "loop" | yes | Which animation |
type |
"fade" | "zoomIn" | "zoomOut" | "slideLeft" | "slideRight" | "slideUp" | "slideDown" | "rise" | "drop" | "pop" | "spin" | "tilt" | "blurIn" | "pulse" | "sway" | "float" | "shake" | "flicker" | "heartbeat" | "typewriter" | "letterRise" | "letterFade" | "wordPop" | "wordRise" | "wordSlide" | "lineRise" | "highlightSweep" | "underlineWipe" | "none" | yes | Animation |
duration |
number | Seconds for the whole animation (loops: one cycle); default 0.5 (0.05..) | |
unit |
"letter" | "word" | "line" | Titles only: play it by each letter, word or line instead of the whole title (the kinetic presets have their own; this overrides it) | |
stagger |
number | Titles, per unit: 0 moves every unit together, 1 one after another; default the preset's, else 0.5 (0..1) | |
order |
"forward" | "backward" | "center" | "random" | Titles, per unit: which unit moves first; default forward (a typewriter's out: backward) |
Example:
{"name":"set_animation","arguments":{"clip_id":"c_123","slot":"in","type":"pop","duration":0.4}}
add_cinematic_bars
Cinematic bars: letterbox the picture to a wide ratio, 2.39 (scope, default), 2.35, 2 or 1.85 (pillarboxed when the canvas is already wider), on any canvas shape. An adjustment layer with the letterbox effect on a Bars track above the footage and hits, below titles, from start (default 0) to end (default the picture's end); animate_in and animate_out slide the bars in and out over that many seconds. Calling it again over the same time replaces the bars. Example: {"ratio": 2.39, "start": 4.0, "animate_in": 0.4}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
ratio |
number | Width over height of the picture left between the bars (default 2.39) (1..4) | |
start |
number | Timeline seconds (default 0) (0..) | |
end |
number | Timeline seconds (default the end of the picture) (0..) | |
animate_in |
number | Seconds the bars take to slide in (default 0: there at once) (0..10) | |
animate_out |
number | Seconds they take to slide out at the end (default 0) (0..10) |
Example:
{"name":"add_cinematic_bars","arguments":{"ratio":2.39,"animate_in":0.6}}
track_motion
Pin a title, sticker or picture to a moving subject in the video under it (CapCut's Tracking): follows the subject from at to the end of the shorter clip on this computer and writes the layer's position keyframes, keeping the layer's offset from the subject. box is the subject in fractions of the video's picture (x, y its top-left corner); without it, a box around the layer's own center. One undoable edit.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The layer to pin (title, sticker, picture or video) |
at |
number | Timeline seconds to start from; default the playhead (0..) | |
box |
{ x, y, w, h } | The subject, as a rectangle of the video's picture | |
box.x |
number | yes | Left edge, a fraction of the picture's width |
box.y |
number | yes | Top edge, a fraction of its height |
box.w |
number | yes | Width, a fraction of the picture's width |
box.h |
number | yes | Height, a fraction of its height |
video_clip_id |
string | The video to follow; default the one under the layer (main track first) |
auto_reframe
Reframe a wide video into another shape on this computer (CapCut's and Premiere's Auto reframe): the canvas takes the ratio (its short side kept), the subject is followed through the clip and the clip fills the canvas with position keyframes that keep it centered. box is the subject in fractions of the picture (x, y its top-left corner) on the clip's first frame; default a square at the center. One undoable edit.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The video clip |
ratio |
"9:16" | "1:1" | "4:5" | "keep" | Canvas shape; keep reframes in the current one | |
box |
{ x, y, w, h } | The subject, as a rectangle of the video's picture | |
box.x |
number | yes | Left edge, a fraction of the picture's width |
box.y |
number | yes | Top edge, a fraction of its height |
box.w |
number | yes | Width, a fraction of the picture's width |
box.h |
number | yes | Height, a fraction of its height |
Example:
{"name":"auto_reframe","arguments":{"clip_id":"c_123","ratio":"9:16"}}
stabilize_clip
Smooth a shaky video clip on this computer (CapCut's Stabilize): measures the camera's shake and moves each frame onto a smooth path, zooming slightly to hide the edges. level: least-crop, recommended (default), most-stable, or off to remove it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
level |
"least-crop" | "recommended" | "most-stable" | "off" | How strongly to smooth |
Graphics and compositing
Shapes (bars, boxes, lines that draw on, soft glows), callouts that ring a part of a screen recording and follow its zooms, overlays pinned to footage, drop shadows, borders and perspective tilt for floating windows and picture-in-picture, track mattes, motion blur, mirrored edges and camera shake.
add_shape
Add a shape layer: a rectangle, rounded_rectangle, ellipse or line, with a solid or two-color gradient fill, an outline and a draw-on amount. The pieces of lower thirds, callouts, CTA buttons (a rounded_rectangle with radius 1 is a pill), progress bars and end cards. It lands above everything at that time (or on track_id), so add a box before the text that sits on it. Sizes are fractions of the canvas; x and y move its center from the canvas center. Every number animates: set_param or add_keyframe on shape.width, shape.height, shape.radius, shape.stroke.width, shape.draw, shape.feather, shape.gradient.angle, shape.gradient.x, shape.gradient.y, shape.gradient.radius, plus the usual transform.*, opacity. Draw a line or outline on by keying shape.draw from 0 to 1. Soft light without image files: an ellipse with feather (a soft edge, pixels) or a radial gradient to transparent is a glow; add a blur effect (amount up to 500) for more. Example lower-third bar: {kind: 'rectangle', start: 1, duration: 4, x: -0.25, y: 0.33, width: 0.4, height: 0.09, color: '#101418'}; a background glow: {kind: 'ellipse', start: 0, duration: 8, width: 0.6, height: 0.8, gradient: {kind: 'radial', from: '#6a5cffcc', to: 'transparent'}, feather: 120}. Overwrite reports overwritten: [{id, kind, what: removed or trimmed}]. Use another track to preserve existing clips.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
kind |
"rectangle" | "rounded_rectangle" | "ellipse" | "line" | yes | Shape |
start |
number | yes | Seconds (0..) |
duration |
number | yes | Seconds (0.001..) |
x |
number | Center's horizontal offset from the canvas center, fraction of its width (-0.5 left edge .. 0.5 right edge; default 0) | |
y |
number | Center's vertical offset, fraction of the canvas height (-0.5 top .. 0.5 bottom; 0.35 is lower-third height; default 0) | |
width |
number | Width, fraction of the canvas width (a line's length); default 0.3, an ellipse 0.2 (0.001..4) | |
height |
number | Height, fraction of the canvas height (unused by lines); default 0.1, an ellipse a circle as wide as width (0.001..4) | |
color |
string | Fill color, CSS: '#ff3366', '#00000099' (with alpha), 'rgba(0,0,0,0.6)', 'white'; a line's color. Default #ffffff | |
gradient |
{ from, to, angle, kind, x, y, radius } | A two-color fill instead of color: linear (default) or radial | |
gradient.from |
string | yes | Color at the start (radial: the center) |
gradient.to |
string | yes | Color at the end (radial: the rim); 'transparent' fades out |
gradient.angle |
number | Linear: degrees, 0 runs left to right (default), 90 top to bottom, 45 diagonal | |
gradient.kind |
"linear" | "radial" | linear (default) or radial: from at the center to to at the rim, an ellipse fitting the box |
|
gradient.x |
number | Radial: the center's move from the box's middle, a fraction of its width (default 0) (-2..2) | |
gradient.y |
number | Radial: the center's move, a fraction of the box's height, down positive (default 0) (-2..2) | |
gradient.radius |
number | Radial: where to is reached, a fraction of the box's half size (default 1, its sides) (0.001..10) |
|
fill |
boolean | false draws only the outline (give stroke_width); default true | |
stroke_color |
string | Outline color, CSS; default white, or color when fill is false | |
stroke_width |
number | Outline width in pixels at a 1080-pixel-tall canvas, centered on the edge; a line's thickness (default 8). Default no outline (0..400) | |
radius |
number | Rectangle corner roundness: 0 square, 1 a pill (radius half the shorter side); rounded_rectangle defaults to 0.35 (0..1) | |
draw |
number | Share of the outline or line drawn, 0..1, from the top center clockwise (a line: from its left end), as After Effects' Trim Paths End; fills are not trimmed. Default 1 (0..1) | |
feather |
number | Soft edge: the shape blurred, its edge fading over this many pixels at 1080p, centered on it, with a Gaussian falloff (0 crisp, default); a feather wider than the shape also dims its middle, as a blur would. An ellipse feathered by half its size or more is a soft glow (0..1000) | |
rotation |
number | Degrees, clockwise (turn a line to any angle) | |
opacity |
number | 0..1, default 1 (0..1) | |
track_id |
string | Track id; default above everything at that time | |
anchor |
{ clip_id, x, y } | Pin it to a point of a clip's picture instead of giving x and y: its center goes there and follows that clip's position, scale and keys (frame_region zooms and pans included) through later edits | |
anchor.clip_id |
string | yes | The clip whose picture it follows (the screen recording or shot under it), on the same timeline |
anchor.x |
number | yes | The point across that clip's source picture: 0 its left edge, 1 its right (frame_region's units) (-1..2) |
anchor.y |
number | yes | The point down it: 0 its top, 1 its bottom (-1..2) |
Example:
{"name":"add_shape","arguments":{"kind":"rounded_rectangle","start":2,"duration":3,"x":-0.25,"y":0.32,"width":0.4,"height":0.1,"color":"#14101fcc","radius":0.3}}
set_shape
Change shape layers made with add_shape (same meanings and units). Values left out keep theirs. Numbers that are animated (have keyframes) get a keyframe at at instead; to start animating one, use add_keyframe on its path (shape.width, shape.height, shape.radius, shape.stroke.width, shape.draw, shape.feather, shape.gradient.angle, shape.gradient.x, shape.gradient.y, shape.gradient.radius). gradient with an empty from removes it; stroke_width 0 removes the outline; feather 0 makes the edge crisp.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Shape clips |
kind |
"rectangle" | "rounded_rectangle" | "ellipse" | "line" | Shape | |
width |
number | Fraction of the canvas width (0.001..4) | |
height |
number | Fraction of the canvas height (0.001..4) | |
color |
string | Fill color, CSS | |
gradient |
{ from, to, angle, kind, x, y, radius } | A two-color fill instead of color: linear or radial | |
gradient.from |
string | yes | Start color (radial: the center); empty removes the gradient |
gradient.to |
string | End color (radial: the rim) | |
gradient.angle |
number | Linear: degrees, 0 left to right | |
gradient.kind |
"linear" | "radial" | linear (default) or radial: from at the center to to at the rim, an ellipse fitting the box |
|
gradient.x |
number | Radial: the center's move from the box's middle, a fraction of its width (default 0) (-2..2) | |
gradient.y |
number | Radial: the center's move, a fraction of the box's height, down positive (default 0) (-2..2) | |
gradient.radius |
number | Radial: where to is reached, a fraction of the box's half size (default 1, its sides) (0.001..10) |
|
fill |
boolean | false: outline only | |
stroke_color |
string | Outline color, CSS | |
stroke_width |
number | Pixels at 1080p; 0 removes the outline (0..400) | |
radius |
number | Rectangle corners 0..1 (0..1) | |
draw |
number | Share drawn 0..1 (0..1) | |
feather |
number | Soft edge, pixels at 1080p; 0 crisp (0..1000) | |
at |
number | Timeline seconds, for animated values |
Example:
{"name":"set_shape","arguments":{"clip_ids":["c_8"],"draw":1,"at":2.6}}
add_callout
Point at a part of a screen recording or shot, as explainers do: a ring, box or rounded box that draws on around region, with the rest of the frame dimmed through a hole the same shape, both pinned to clip_id's picture, so they stay on the part through that clip's zooms and pans (frame_region, keys) and any later edit of them; to glides them on to other parts. region is in fractions of that clip's source picture, as frame_region takes it (x, y its top-left corner): give the part itself, the ring is drawn a few pixels clear of it. Two shape layers, the dim under the ring, grouped (they move, copy and delete together; each edits like any shape), above everything at that time. Example, the Split button of a 2560x1440 recording, then the clip's edge: {clip_id: 'c_1', region: {x: 0.102, y: 0.542, w: 0.018, h: 0.03}, at: 14.2, duration: 4.3, to: [{region: {x: 0.515, y: 0.825, w: 0.06, h: 0.095}, at: 15.83}], sound: true}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The footage it points at (a video or picture clip on the same timeline) |
region |
{ x, y, w, h } | yes | The part to point at, in fractions of that clip's source picture: x, y its top-left corner |
region.x |
number | yes | Left edge, a fraction of the picture's width (0..1) |
region.y |
number | yes | Top edge, a fraction of its height (0..1) |
region.w |
number | yes | Width, a fraction of the picture's width (0.01..1) |
region.h |
number | yes | Height, a fraction of its height (0.01..1) |
shape |
"ring" | "box" | "rounded" | ring (an ellipse around it), box, or rounded (a box with round corners; default) | |
dim |
number | How dark the rest of the frame goes, 0 (no dim, only the ring) .. 1; default 0.5 (0..1) | |
draw_on |
number | Seconds the ring takes to draw on around it; 0 shows it whole at once. Default 0.35 (0..5) | |
at |
number | yes | Timeline seconds it starts (0..) |
duration |
number | Seconds it stays; default 2 (0.1..) | |
to |
{ region, at, glide, sound }[] | Glides, in order: each reaches its region at its at (timeline seconds), moving there from where it was over glide seconds | |
to[].region |
{ x, y, w, h } | yes | The next part, in the same clip's source fractions |
to[].region.x |
number | yes | Left edge, a fraction of the picture's width (0..1) |
to[].region.y |
number | yes | Top edge, a fraction of its height (0..1) |
to[].region.w |
number | yes | Width, a fraction of the picture's width (0.01..1) |
to[].region.h |
number | yes | Height, a fraction of its height (0.01..1) |
to[].at |
number | yes | Timeline seconds it arrives there (0..) |
to[].glide |
number | Seconds the move takes, ending at at; default 0.4 (0.03..10) | |
to[].sound |
boolean | A soft click as it arrives there; default the callout's sound | |
color |
string | The ring's color, CSS; default the brand kit's accent | |
stroke_width |
number | The ring's outline, pixels at 1080p; default 6 (1..40) | |
sound |
boolean | A soft click as the ring draws on and as it arrives at each to (a to's own sound: false leaves that one silent, true clicks it alone), on the effects track (default false). Made on this computer: not inside a batch unless the project has a click already |
Example:
{"name":"add_callout","arguments":{"clip_id":"c_rec","region":{"x":0.102,"y":0.542,"w":0.018,"h":0.03},"at":14.2,"duration":4.3,"dim":0.5,"to":[{"region":{"x":0.515,"y":0.825,"w":0.06,"h":0.095},"at":15.83}],"sound":true}}
set_anchor
Pin titles, shapes or stickers to a point of a clip's picture (anchor {clip_id, x, y}, source fractions as frame_region takes them), so they follow its zooms and pans; or none true to unpin them where they are. A pinned clip moved by hand (a drag, set_param on its position) is pinned again where it lands. describe_project shows each clip's anchor.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | The clips to pin or unpin (a callout's two parts: give both) |
anchor |
{ clip_id, x, y } | Pin it to a point of a clip's picture instead of giving x and y: its center goes there and follows that clip's position, scale and keys (frame_region zooms and pans included) through later edits | |
anchor.clip_id |
string | yes | The clip whose picture it follows (the screen recording or shot under it), on the same timeline |
anchor.x |
number | yes | The point across that clip's source picture: 0 its left edge, 1 its right (frame_region's units) (-1..2) |
anchor.y |
number | yes | The point down it: 0 its top, 1 its bottom (-1..2) |
none |
boolean | true: unpin them; they stay where they are now |
set_shadow
A drop shadow under a picture: a video, image, title, shape or compound clip. It is cast by the finished picture, after its mask, crop, effects and track matte, so a rounded mask casts a rounded shadow and a compound clip (a floating UI window built from footage, a border and a bar) casts one shadow for the whole window. color (CSS, default black), blur: softness in pixels at 1080p, 0..600 (twice the Gaussian's sigma, as CSS box-shadow; default 60), x and y: how far it falls, pixels at 1080p, y down (default 0, 24), opacity 0..1 (default 0.5). A floating window: {blur: 80, y: 30, opacity: 0.55}; a contact shadow: {blur: 12, y: 4, opacity: 0.6}. Values left out keep theirs (or the defaults for a new shadow). Animate with add_keyframe on shadow.blur, shadow.x, shadow.y, shadow.opacity; values that are keyframed get a key at at instead. off: true removes it and its keyframes. Titles also have style.shadow, drawn with the letters; this one is cast by the whole title, box included.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
color |
string | CSS color; its alpha multiplies opacity | |
blur |
number | Softness, pixels at 1080p (0 hard-edged) (0..600) | |
x |
number | Horizontal fall, pixels at 1080p, right positive (-2000..2000) | |
y |
number | Vertical fall, pixels at 1080p, down positive (-2000..2000) | |
opacity |
number | 0..1 (0..1) | |
off |
boolean | Remove the shadow and its keyframes | |
at |
number | Timeline seconds, for keyframed values |
Example:
{"name":"set_shadow","arguments":{"clip_ids":["c_6"],"blur":80,"y":30,"opacity":0.55}}
set_border
A border (stroke) around a picture: a video, image, title, shape or compound clip, as picture-in-picture shots and social layouts frame theirs. It follows the finished picture's edge, after its mask, crop, effects and track matte, outside it: a rounded mask gets a rounded border, a circle mask a ring, a compound clip one border around the whole window; set_shadow's shadow is cast by the picture and its border together. color (CSS, default white), width: pixels at 1080p outside the edge, 0..200 (default 8; it stays that wide however the clip is scaled), opacity 0..1 (default 1; the clip's opacity fades it too). A framed inset: {width: 10, color: '#ffffff'}; a thin dark keyline: {width: 2, color: '#000000', opacity: 0.6}. Values left out keep theirs (or the defaults for a new border). Animate with add_keyframe on border.width, border.opacity; values that are keyframed get a key at at instead. off: true removes it and its keyframes. Titles and shapes also have their own outline (style.stroke, stroke_width), drawn with the letters or the shape; this one goes around the whole finished picture, a title's box included.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
color |
string | CSS color; its alpha multiplies opacity | |
width |
number | Pixels at 1080p outside the picture's edge (0 shows none) (0..200) | |
opacity |
number | 0..1 (0..1) | |
off |
boolean | Remove the border and its keyframes | |
at |
number | Timeline seconds, for keyframed values |
Example:
{"name":"set_border","arguments":{"clip_ids":["c_6"],"width":10,"color":"#ffffff"}}
set_tilt
Tilt a picture in perspective, as product films turn floating UI windows in depth (CSS rotateX and rotateY): x tips the top away (negative: the bottom), y turns the right side away (negative: the left), in degrees, seen from perspective pixels at 1080p in front of the canvas center (default 2000; 800 is dramatic, 4000 gentle). A window at rest, three-quarter view: {y: -18, x: 6}. Values left out keep theirs; x and y 0 lay it flat. Animate with add_keyframe on transform.rotateX, transform.rotateY, transform.perspective (a window swinging into place: y from -35 to 0); keyframed values get a key at at. A compound clip tilts as one picture, its shadow (set_shadow) cast by the tilted window.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
x |
number | Degrees about the horizontal axis: positive tips the top away (-89..89) | |
y |
number | Degrees about the vertical axis: positive turns the right side away (-89..89) | |
perspective |
number | The camera's distance, pixels at 1080p (default 2000): nearer is stronger (100..100000) | |
at |
number | Timeline seconds, for keyframed values |
Example:
{"name":"set_tilt","arguments":{"clip_ids":["c_6"],"y":-18,"x":6}}
set_track_matte
Cut a clip with a track matte (After Effects'): it shows only where another clip's picture is, the basis of video inside text, logo reveals and wipe reveals. mode alpha (default) keeps where the matte is opaque, alphaInverted where it is transparent, luma where it is bright, lumaInverted where it is dark. matte_clip_id: a visual clip of the same timeline (text, shape, picture, video), 'above' for whatever is on the video track directly above at each frame (keeps working after cuts), or 'none' to remove the matte; left out, the current matte stays (a new one uses 'above'). The matte clip is not drawn itself but keeps its transform, animations and keyframes, so animating the matte animates the reveal. Example, video inside big text: add_text {text: 'SUMMER', style: {size: 420}} then set_track_matte {clip_id: , matte_clip_id: }.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The clip to cut (it is what shows) |
matte_clip_id |
string | The matte clip's id, 'above' (the video track directly above) or 'none' | |
mode |
"alpha" | "alphaInverted" | "luma" | "lumaInverted" | alpha: show where the matte is opaque (default); alphaInverted: where it is not; luma: where it is bright; lumaInverted: where it is dark |
Example:
{"name":"set_track_matte","arguments":{"clip_id":"c_2","matte_clip_id":"above","mode":"alpha"}}
set_motion_blur
Motion blur from movement (After Effects'): while a clip moves (position, scale or rotation keyframes, wiggle, in/out animations, and moving transitions such as slides, pushes, zooms and spins), its picture is averaged over the camera's shutter, so fast moves streak instead of strobing. shutter_angle in degrees: 180 (default; film's and After Effects') blurs over half a frame centered on it, 360 a whole frame (heavier), 90 crisper. Still frames cost nothing; export and the paused viewer use up to 32 samples, playback fewer.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
on |
boolean | yes | Motion blur on or off |
shutter_angle |
number | Degrees, 1..720; default 180 (1..720) |
Example:
{"name":"set_motion_blur","arguments":{"clip_ids":["c_4","c_5"],"on":true,"shutter_angle":180}}
set_edge_mode
What fills the canvas around a picture that is moved, shrunk, spun or shaken off it (After Effects' Motion Tile with Mirror Edges): mirror reflects the picture at its edges, seamless, the usual choice under camera shake, zoom-out and spin moves; repeat tiles it; none (default) leaves the canvas empty there. Pairs with set_wiggle and with scale below 1.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
edge |
"none" | "mirror" | "repeat" | yes | Edge mode |
Example:
{"name":"set_edge_mode","arguments":{"clip_ids":["c_4"],"edge":"mirror"}}
set_wiggle
Organic shake or drift (After Effects' wiggle): seeded smooth noise added to a clip's position, x, y, rotation or scale, for handheld camera shake, a nervous title or a floating logo. frequency is wiggles per second (camera shake 2..6, a slow drift 0.3..1); amplitude is the largest offset: pixels for position/x/y, degrees for rotation, percent for scale (a light shake: position 12 px and rotation 0.6 degrees at 4 Hz; an impact: position 40 px). amplitude 0 removes it. strength 0..1 (default 1) scales it: with at, it is keyed there, so two calls ramp a shake in or out (or add_keyframe on wiggle.x.strength, wiggle.y.strength, wiggle.rotation.strength, wiggle.scale.strength). The same seed repeats the same path. A shaken picture uncovers the canvas edge: set_edge_mode mirror, or scale it up slightly.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual clips |
target |
"position" | "x" | "y" | "rotation" | "scale" | yes | What shakes: position is x and y together |
frequency |
number | Wiggles per second (Hz); default 3, or the current one (0.05..30) | |
amplitude |
number | Largest offset: pixels (position, x, y), degrees (rotation) or percent (scale); 0 removes the wiggle (0..) | |
strength |
number | 0..1, multiplies the amplitude; keyed at at when given (0..1) |
|
seed |
integer | Noise seed; default one per clip | |
at |
number | Timeline seconds: key strength there |
Example:
{"name":"set_wiggle","arguments":{"clip_ids":["c_4"],"target":"position","frequency":4,"amplitude":12}}
Speed
Constant speed, ramps, reverse and smoother slow motion.
set_speed
Constant speed for a video or audio clip (2 = twice as fast, 0.5 = slow motion). The clip's length changes; later clips on its track move with it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
speed |
number | yes | Multiplier (0.05..100) |
set_speed_curve
Ramp a video or audio clip's speed (CapCut's Curve speed): a preset, or points over the clip with t from 0 (its start) to 1 (its end) and speed 0.1..10. The rate changes smoothly (exponentially) between points. The clip keeps its source range, so its length becomes source length / mean speed and later clips on its track move. preset 'none' returns to the constant speed.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
preset |
"none" | "custom" | "montage" | "hero" | "bullet" | "jumpCut" | "flashIn" | "flashOut" | A ready-made ramp | |
points |
{ t, speed }[] | Speed points across the clip; used when no preset is given | |
points[].t |
number | yes | 0 = clip start, 1 = clip end (0..1) |
points[].speed |
number | yes | Rate there (0.1..10) |
Example:
{"name":"set_speed_curve","arguments":{"clip_id":"c_123","preset":"hero"}}
set_speed_ramp
Velocity edit: ramp a video clip's speed so its slow-motion moment (moment slow, default) or its fast moment (fast) lands exactly on a beat at at (timeline seconds; default the Beat marker nearest the clip's middle). The clip keeps its start and length, so cuts made on the beat stay there; the ramp reads more or less of the media instead (the in-point moves earlier when the file would run out). Without a preset: speed 0.1..10 at the moment (default 0.3 slow, 3 fast), base elsewhere (default 2 slow, 1 fast), easing over ease seconds (default 0.12) into the beat and holding hold seconds (default 0.25). Or preset (montage, hero, bullet, jumpCut, flashIn, flashOut): its slowest (or fastest) point is moved onto the beat. set_speed_curve ramps a whole clip without aligning it. Example: {"clip_id": "c_5", "at": 3.75}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
at |
number | Timeline seconds of the beat, inside the clip (0..) | |
moment |
"slow" | "fast" | Which moment lands on the beat | |
speed |
number | Speed at the moment (0.1..10) | |
base |
number | Speed before and after it (0.1..10) | |
hold |
number | Seconds the moment holds after the beat (0..10) | |
ease |
number | Seconds the speed takes to change into and out of the moment (0.02..5) | |
preset |
"montage" | "hero" | "bullet" | "jumpCut" | "flashIn" | "flashOut" | A ready-made ramp to align instead |
Example:
{"name":"set_speed_ramp","arguments":{"clip_id":"c_7","at":8.4,"moment":"slow"}}
set_reverse
Play a video or audio clip backward (reverse=true) or forward again. The clip keeps its length and source range; its sound reverses too.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
reverse |
boolean | yes | Backward |
set_preserve_pitch
Keep a sped-up or slowed-down clip's pitch natural (preserve=true, the default) or let it rise and fall with the speed like tape (preserve=false, CapCut's Change pitch).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
preserve |
boolean | yes | Keep the original pitch |
set_frame_blending
Frame blending (Premiere's): a slowed video clip, or one whose source rate differs from the project's, mixes neighboring source frames instead of repeating them, so slow motion looks smoother.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video clips |
on |
boolean | yes | Blend frames |
Text and captions
Titles, templates, stickers, captions and the transcript.
add_text
Add a text (title) clip. Position x/y are offsets from the canvas center as fractions of its width/height (y=0.35 is near the bottom). It lands above everything at that time (titles, shapes, a grade or beat hits), sharing a free titles track when one is there; on track_id it goes where asked, and the answer notes an adjustment layer above it (its grade or zoom applies to the title too). Overwrite reports overwritten: [{id, kind, what: removed or trimmed}]. Use another track to preserve existing clips. Lines wrap inside the title-safe area by default (style.maxWidth): a centered title within 90% of the width, on 9:16 within the column the apps' buttons leave, one off center within twice the room on its nearer side; breaks a line where you choose.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
text |
string | yes | The words to show; \n for line breaks |
start |
number | yes | Seconds (0..) |
duration |
number | yes | Seconds (0.001..) |
x |
number | Horizontal offset, -0.5..0.5 | |
y |
number | Vertical offset, -0.5..0.5 | |
style |
{ font, size, color, bold, weight, italic, align, highlight, case, background, stroke, stroke2, shadow, glow, gradient, accent, letterSpacing, lineHeight, maxWidth } | The text's look; fields left out keep their value (or the default look for new text) | |
style.font |
string | Font family | |
style.size |
number | Pixels at 1080p canvas height (4..) | |
style.color |
string | CSS color | |
style.bold |
boolean | Bold (weight 700); setting it clears weight | |
style.weight |
number | Font weight: 100 thin, 300 light, 400 regular, 500 medium, 600 semibold, 700 bold, 800 extra bold, 900 black. Wins over bold; a weight the font lacks draws its nearest face. Pair a heavy display line with a light one (100..900) | |
style.italic |
boolean | Italic | |
style.align |
"left" | "center" | "right" | Alignment | |
style.highlight |
string | Captions: color of the word being spoken; an empty string turns it off | |
style.case |
"none" | "upper" | "lower" | "title" | Letter case as shown (the text is kept): none, upper, lower or title | |
style.background |
{ color, padding, radius } | A box behind the text, so it reads over busy footage; an empty color removes it | |
style.background.color |
string | yes | CSS color, e.g. rgba(0,0,0,0.6) |
style.background.padding |
number | Pixels (0..) | |
style.background.radius |
number | Corner radius, pixels (0..) | |
style.stroke |
{ color, width } | An outline around the letters (6-10 for titles over footage); width 0 or an empty color removes it | |
style.stroke.color |
string | yes | CSS color |
style.stroke.width |
number | Pixels (0..) | |
style.stroke2 |
{ color, width } | A second outline outside the first, for a sticker-like double edge | |
style.stroke2.color |
string | yes | CSS color |
style.stroke2.width |
number | Pixels beyond the first outline; default 6 (0..) | |
style.shadow |
{ color, blur, x, y } | A drop shadow; an empty color removes it | |
style.shadow.color |
string | yes | CSS color |
style.shadow.blur |
number | Pixels (0..) | |
style.shadow.x |
number | Pixels | |
style.shadow.y |
number | Pixels | |
style.glow |
{ color, blur, strength } | A neon glow around the letters | |
style.glow.color |
string | yes | CSS color, e.g. #00e5ff |
style.glow.blur |
number | Pixels, default 24 (0..) | |
style.glow.strength |
number | 1..3, how bright; default 1 (1..3) | |
style.gradient |
{ from, to, angle } | A two-color fill instead of color | |
style.gradient.from |
string | yes | CSS color at the start |
style.gradient.to |
string | yes | CSS color at the end |
style.gradient.angle |
number | Degrees: 0 left to right (default), 90 top to bottom (-360..360) | |
style.accent |
string | Color the highlightSweep marker and the underlineWipe line draw in; an empty string returns to the default | |
style.letterSpacing |
number | Pixels between letters at 1080p (wide-tracked labels: 6 to 20) (-20..200) | |
style.lineHeight |
number | Line spacing, a multiple of the size (default 1.2; 0.9-1.0 for tight display headlines) (0.5..3) | |
style.maxWidth |
number | Lines wrap past this fraction of the canvas width; a new title defaults to the safe width where it stands (0.9 centered, 0.62 centered on 9:16, clear of the apps' buttons) (0.1..1) | |
role |
"headline" | "subhead" | "body" | "label" | "cta" | "badge" | Style it from the brand kit (set_brand) first, then apply style on top: headline (heading font, about 9% of the frame high, with a shadow), subhead, body, label (small capitals in the accent color), cta (a pill button in the primary color) or badge (a box in the accent color) | |
track_id |
string | Track id; default above everything at that time | |
anchor |
{ clip_id, x, y } | Pin it to a point of a clip's picture instead of giving x and y: its center goes there and follows that clip's position, scale and keys (frame_region zooms and pans included) through later edits | |
anchor.clip_id |
string | yes | The clip whose picture it follows (the screen recording or shot under it), on the same timeline |
anchor.x |
number | yes | The point across that clip's source picture: 0 its left edge, 1 its right (frame_region's units) (-1..2) |
anchor.y |
number | yes | The point down it: 0 its top, 1 its bottom (-1..2) |
Example:
{"name":"add_text","arguments":{"text":"50% OFF","start":1,"duration":2.5,"y":-0.1,"style":{"size":160,"bold":true,"color":"#ffffff","font":"Arial Black","stroke":{"color":"#000000","width":8}}}}
set_text
Change text clips' style with clip_ids (or clip_id). New text requires exactly one clip. Styled runs keep their positions for same-length changes; otherwise surviving characters carry their style.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | Text clip id; alternatively use clip_ids | |
clip_ids |
string[] | Text clips receiving the same style; text requires one clip | |
text |
string | New words; one clip only | |
style |
{ font, size, color, bold, weight, italic, align, highlight, case, background, stroke, stroke2, shadow, glow, gradient, accent, letterSpacing, lineHeight, maxWidth } | The text's look; fields left out keep their value (or the default look for new text); its fields are add_text's style (help add_text) |
Example:
{"name":"set_text","arguments":{"clip_ids":["c_123"],"style":{"background":{"color":"#000000b3","padding":18,"radius":12}}}}
style_text_range
Style characters within a title. UTF-16 offsets are end-exclusive. Alternatively match a literal word or phrase (case-sensitive), choosing a 1-based occurrence or all matches. The style is color, gradient, weight, italic, underline and scale, given flat ({match: 'SALE', color: '#ff0'}) or as a style object as set_text takes one ({match: 'SALE', style: {color: '#ff0'}}). Later styles merge fields; a color replaces a gradient; a scaled run wraps at its own size. clear removes styling in the range, or all runs if no range is supplied. describe_project reports runs.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Text clip id |
match |
string | Literal word or phrase to find | |
occurrence |
number | 1-based match occurrence; default 1 (1..) | |
all |
boolean | Style every match | |
start |
number | Start UTF-16 character offset, inclusive (0..) | |
end |
number | End UTF-16 character offset, exclusive (1..) | |
color |
string | CSS fill color; replaces the range gradient | |
gradient |
{ from, to, angle } | Linear fill gradient | |
gradient.from |
string | yes | Start CSS color |
gradient.to |
string | yes | End CSS color |
gradient.angle |
number | yes | Angle in degrees |
weight |
number | Font weight (100..900) | |
italic |
boolean | Italic letters | |
underline |
boolean | Underline letters | |
scale |
number | Size multiplier relative to the base size (0.25..4) | |
style |
{ color, gradient, weight, italic, underline, scale } | The same fields as one object, as set_text takes its style; the flat ones win | |
style.color |
string | CSS fill color; replaces the range gradient | |
style.gradient |
{ from, to, angle } | Linear fill gradient | |
style.gradient.from |
string | yes | Start CSS color |
style.gradient.to |
string | yes | End CSS color |
style.gradient.angle |
number | yes | Angle in degrees |
style.weight |
number | Font weight (100..900) | |
style.italic |
boolean | Italic letters | |
style.underline |
boolean | Underline letters | |
style.scale |
number | Size multiplier relative to the base size (0.25..4) | |
clear |
boolean | Remove styles from the range, or the entire title without a range |
animate_text_style
Animate a title's size, letter spacing and color with keyframes: each key sets any of them at a timeline time, and the title moves between keys in the key's ease (the curve toward the next one). A tracking expand: [{at: 1, letter_spacing: 0, ease: 'easeOut'}, {at: 2.5, letter_spacing: 24}]; a title that grows and turns accent-colored: [{at: 0, size: 90, color: '#ffffff'}, {at: 0.6, size: 120, color: '#ffd23f'}]. Keys at a time that already has one replace it. size: pixels at 1080p; letter_spacing: pixels at 1080p; color: CSS (keyed colors are opaque: fade the title with opacity). add_keyframe also keys style.size and style.letterSpacing; style.color keys hold 0xRRGGBB numbers. remove_keyframes on style.size, style.letterSpacing or style.color stops one animating.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Text clip id |
keys |
{ at, size, letter_spacing, color, ease }[] | yes | Keys, each at a timeline time |
keys[].at |
number | yes | Timeline seconds |
keys[].size |
number | Font size, pixels at 1080p (4..) | |
keys[].letter_spacing |
number | Pixels between letters at 1080p (-20..400) | |
keys[].color |
string | CSS color | |
keys[].ease |
"linear" | "hold" | "easeIn" | "easeOut" | "easeInOut" | "smooth" | "fastIn" | "overshoot" | "anticipate" | "spring" | "elastic" | "bounce" | Curve toward the next keyframe: linear, hold, easeIn, easeOut, easeInOut, smooth, fastIn, overshoot, anticipate, spring, elastic, bounce. fastIn is the modern expo-out (arrives fast, glides to rest); spring, elastic and bounce overshoot and settle |
Example:
{"name":"animate_text_style","arguments":{"clip_id":"c_5","keys":[{"at":1,"letter_spacing":0,"ease":"easeOut"},{"at":2.5,"letter_spacing":24}]}}
add_text_template
Add a text template: a title design of text and box layers with in/out/loop animations, stacked above everything at that time. Templates: lowerThird, nameTag, subscribe, chapter, quote, countdown, location, opener, news, social, endCard, linkInBio. Word art (one styled text): neonPink, neonBlue, retro, comic, hollow, gold, fire, frost, yellow, hardShadow, candy, sticker, chroma, minimal, typewriter, highlight. texts replaces the template's words, text layers in order (bottom first).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
string | yes | Template key or label |
start |
number | yes | Seconds (0..) |
texts |
string[] | Words for the text layers, in order |
Example:
{"name":"add_text_template","arguments":{"name":"lowerThird","start":2,"texts":["Jordan Lee","Head of Design"]}}
apply_word_art
Restyle text clips with a word-art look (fill, stroke, shadow, box), keeping their words, size and position. name is a Word art key from add_text_template.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Text clips |
name |
string | yes | Word art key or label |
add_sticker
Add a sticker from the built-in library (emoji, or drawn arrows, circles, underlines, bubbles, badges) as an image overlay above everything at that time. name is a sticker id, label or the emoji itself: 'fire', 'thumbs-up', 'curved-arrow-red', 'circle-yellow', 'subscribe', '🎉'. window.videoEditor.stickers() lists them; window.videoEditor.exec draws the sticker's image before placing it. Overwrite reports overwritten: [{id, kind, what: removed or trimmed}]. Use another track to preserve existing clips.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
string | yes | Sticker id, label or emoji |
start |
number | yes | Seconds (0..) |
duration |
number | Seconds; default 3 (0.04..) | |
x |
number | Horizontal offset from center, -0.5..0.5 | |
y |
number | Vertical offset from center, -0.5..0.5 | |
scale |
number | 1 = fit the canvas; default 0.3 (0.01..) | |
motion |
"still" | "pop" | "float" | "pulse" | "sway" | "heartbeat" | Entrance and loop animation |
add_captions
Add captions (subtitles) as caption text clips on a Captions track. Times are timeline seconds. replace=true removes existing captions first. Captions export as SRT/VTT and restyle together.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
captions |
{ start, end, text, words }[] | yes | The captions, in order |
captions[].start |
number | yes | Seconds (0..) |
captions[].end |
number | yes | Seconds (0..) |
captions[].text |
string | yes | Words shown |
captions[].words |
number[] | Seconds each word of text starts, for the spoken-word highlight | |
style |
{ font, size, color, bold, weight, italic, align, highlight, case, background, stroke, stroke2, shadow, glow, gradient, accent, letterSpacing, lineHeight, maxWidth } | The text's look; fields left out keep their value (or the default look for new text); its fields are add_text's style (help add_text) | |
y |
number | Vertical offset from center as a canvas fraction; default 0.36 near the bottom, or 0.05 on a tall canvas, above the apps' captions and buttons | |
replace |
boolean | Remove existing captions first |
set_caption_look
Restyle every caption at once (CapCut's caption templates, and its caption panel's position and size). look gives them one of the built-in looks: classic, yellow (bold yellow capitals), boxed, pop (capitals with the spoken word highlighted), neon or minimal, which sets the whole caption style and its height. y and x move every caption, size and maxWidth size and narrow them, and style sets any other field of their style (add_text's: color, highlight, font, stroke...), over the look when both are given. Words and timings stay. Example: {y: -0.2, size: 52, maxWidth: 0.6} puts a tall video's captions in its upper band, smaller and narrower.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
look |
"classic" | "yellow" | "boxed" | "pop" | "neon" | "minimal" | Look | |
y |
number | Every caption's vertical offset from the center, a canvas fraction (y down; 0.05 on a tall canvas sits above the apps' own captions and buttons) (-0.5..0.5) | |
x |
number | Every caption's horizontal offset from the center, a canvas fraction (-0.04 centers them between a tall canvas's app rails) (-0.5..0.5) | |
size |
number | Every caption's type size, pixels at 1080p (1..) | |
maxWidth |
number | The width every caption wraps within, a fraction of the canvas width (0.05..1) | |
style |
{ font, size, color, bold, weight, italic, align, highlight, case, background, stroke, stroke2, shadow, glow, gradient, accent, letterSpacing, lineHeight, maxWidth } | The text's look; fields left out keep their value (or the default look for new text); its fields are add_text's style (help add_text) |
Example:
{"name":"set_caption_look","arguments":{"look":"pop","y":-0.2,"size":52,"maxWidth":0.6}}
replace_caption_text
Find and replace in every caption's text (CapCut's caption Find and replace), e.g. to fix a name speech recognition misspelled. Any case unless match_case; whole_word skips matches inside longer words.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
find |
string | yes | Text to find |
replace |
string | yes | Replacement (may be empty) |
match_case |
boolean | Match case | |
whole_word |
boolean | Whole words only |
get_transcript
Hear a video or audio clip's speech on this machine (Whisper) and return its words with source-second times. Delete words by passing their spans to cut_ranges (Premiere's text-based editing).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
Brand and ad elements
A brand kit every part of an ad draws from, built ad elements, and titles behind the subject.
set_brand
Set the project's brand kit, which add_text's role, apply_brand and add_ad_element draw with, so every part of an ad agrees: colors by role (CSS colors), a heading and a body font (at most two families), logo pictures by variant (asset ids from import_media; a PNG with transparency works best), the brand's name, website and tagline. Only what is given changes; an empty string removes a logo, name, url or tagline; reset clears the kit. describe_project shows it. Example: {primary: '#ff3366', accent: '#ffd23f', background: '#14101f', heading_font: 'Arial Black', body_font: 'Segoe UI', logo: 'a_123', url: 'shop.example.com'}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
string | Brand name (a wordmark when there is no logo) | |
primary |
string | Main color: buttons, bars | |
secondary |
string | Second color: panels, a lower third's second line | |
accent |
string | Highlight color: badges, labels, marker sweeps | |
background |
string | Behind end cards and text boxes (usually dark) | |
text |
string | Text over footage and the background (usually white) | |
heading_font |
string | Font family for headlines, buttons, badges | |
body_font |
string | Font family for everything else | |
logo |
string | Main logo: an image asset id | |
logo_light |
string | Logo for dark backgrounds: an image asset id | |
logo_dark |
string | Logo for light backgrounds: an image asset id | |
logo_icon |
string | The mark alone: an image asset id | |
url |
string | Website shown on end cards, e.g. shop.example.com | |
tagline |
string | A short line: end cards and lower thirds use it by default | |
reset |
boolean | Clear the brand kit (then apply the rest of this call) |
Example:
{"name":"set_brand","arguments":{"name":"Northwind","primary":"#1f6feb","accent":"#ffd23f","background":"#0d1117","text":"#ffffff","heading_font":"Arial Black","body_font":"Segoe UI","logo":"a_9","url":"northwind.example","tagline":"Run further"}}
apply_brand
Restyle clips with the brand kit, keeping their words, timing and place. Titles: with role, that role's whole look and size (headline, subhead, body, label, cta, badge, as add_text's role); without, only fonts and colors change (the heading font for bold display type; a box takes the primary color with a readable label; plain type the brand's text color; glows, gradients, sweeps and spoken-word highlights the accent). Solid color clips: role primary, secondary, accent, background or text sets their color (default primary).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Text or color clips |
role |
"headline" | "subhead" | "body" | "label" | "cta" | "badge" | "primary" | "secondary" | "accent" | "background" | "text" | A text role, or a color role for solids |
add_ad_element
Add a finished ad element in the brand kit's colors and fonts (set_brand first; neutral defaults otherwise), laid out inside the title-safe area (on 9:16, clear of the apps' buttons and captions) and clear of the captions showing then, animated in, its clips grouped on rows of their own above everything at that time and under the captions (no other clip shares them, so moving another track never reorders its parts). Elements: end_card (a solid card, background brand/primary/scrim/none, with the logo, headline, a call-to-action button and the url; always centered; put it last), lower_third (a name in a box over a title or role in the primary color; hold it 3 to 6 s), offer_badge (headline is the offer, e.g. '-30%' or '$49', in an accent box, tilted, pulsing; subline a small tag under it, e.g. 'Today only'), cta_button (a pill button, e.g. 'Shop now', pulsing; subline or url a line under it), logo_reveal (the logo pops in, optional glow flare and white flash, with headline or the brand name as a wordmark and the tagline). Centered elements sit on the frame's center line, a badge stays in its half, an element too big for its space is built smaller as a whole, and no text goes under the readable floor (4.5% of the frame's short side, 5% on a tall frame: size 49 on 16:9, 31 on 9:16). Every part is an ordinary clip to edit afterwards. Example: {element: 'end_card', start: 12, headline: 'Fresh roast, every morning', cta: 'Order now'}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
element |
"end_card" | "lower_third" | "offer_badge" | "cta_button" | "logo_reveal" | yes | Which element |
start |
number | yes | Timeline seconds (0..) |
duration |
number | Seconds; default end_card 3, lower_third 4, offer_badge 3, cta_button 3, logo_reveal 2.5 (0.2..) | |
headline |
string | end_card headline; lower_third name; offer_badge offer; logo_reveal wordmark (default the brand name when there is no logo) | |
subline |
string | lower_third title or role; offer_badge tag; cta_button line under it; logo_reveal line under the wordmark (default the tagline) | |
cta |
string | Button words for end_card and cta_button; default 'Shop now' | |
url |
string | end_card and cta_button: website line; default the brand's url | |
logo |
"auto" | "main" | "light" | "dark" | "icon" | "none" | Which logo variant: auto (light on dark, dark on light), main, light, dark, icon, or none. end_card and logo_reveal draw one by default (auto); lower_third only when given, left of the name and title and as tall as both; offer_badge and cta_button draw none and refuse it | |
position |
"top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right" | Where in the safe area: lower_third default bottom-left, offer_badge top-right, cta_button bottom, logo_reveal center (end_card is always centered) | |
background |
"brand" | "primary" | "scrim" | "none" | end_card (default brand) and logo_reveal (default none): brand background color, primary color, scrim (60% black over the footage) or none | |
glow |
boolean | logo_reveal: a glow that flares as the logo lands | |
flash |
boolean | logo_reveal: a white flash as the logo lands |
Example:
{"name":"add_ad_element","arguments":{"element":"end_card","start":12,"headline":"Run further","cta":"Shop now"}}
text_behind_subject
Put titles (or stickers) behind the person or product in a video or picture clip, the 'text behind the subject' look: the clip's background is removed on this computer (a video's matte as Remove background makes it; a picture is cut out once into a new picture; the first run downloads the model), and a copy showing only the subject goes on a new track right above the titles' track, silent and grouped with the original so they move together; an adjustment layer between the copy and the clip (a grade kept under the titles) would not reach the copy, so the copy takes its grade (look, sliders, LUT, effects) as its own. The answer says where the copy went (placed) and what grade it took (graded, note). Titles below the clip are lifted above it first. Works best when a large title overlaps the subject. One undoable edit; answers the copy's id (subjectClipId).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The video or picture clip with the person or product |
text_clip_ids |
string[] | yes | The titles (or stickers) to put behind the subject; they must play while the video does |
Example:
{"name":"text_behind_subject","arguments":{"clip_id":"c_2","text_clip_ids":["c_5"]}}
Effects, transitions and color
Effects stacks, transitions at cuts, looks and the color grade.
add_effect
Add a video effect to a visual or adjustment clip. params override the effect's defaults. Keep the strobe flash (flash) at 3 flashes a second or slower: faster flashing can trigger seizures (WCAG 2.3.1).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
type |
"blur" | "zoomBlur" | "pixelate" | "rgbSplit" | "glitch" | "vhs" | "grain" | "scanlines" | "invert" | "posterize" | "duotone" | "mirror" | "kaleidoscope" | "fisheye" | "shake" | "zoomPulse" | "zoom" | "flash" | "glow" | "lightLeak" | "denoise" | "motionBlur" | "sketch" | "halftone" | "letterbox" | "magnify" | yes | Effect |
params |
object | Effect parameters |
Example:
{"name":"add_effect","arguments":{"clip_id":"c_123","type":"glow","params":{"amount":60,"threshold":88}}}
edit_effects
Manage a clip's effect stack. Move/reset/remove need effect_id; enable/disable without it affects all effects. Clear removes the entire stack. Reset/remove/clear also remove the affected effect keyframes; other settings stay unchanged.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
action |
"move_up" | "move_down" | "reset" | "remove" | "clear" | "enable" | "disable" | yes | Stack action |
effect_id |
string | Effect id from effectStack in describeProject |
copy_effects
Append a source clip's effect stack and effect keyframes to target clips. Each copy gets fresh effect IDs; existing effects, grading, transforms and clip timing stay intact. Animation keeps clip-relative timing, including keys beyond a shorter target's end. Locked or nonvisual targets are rejected.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
source_clip_id |
string | yes | Clip whose effects to reuse |
clip_ids |
string[] | yes | Target visual or adjustment clip IDs |
apply_effect_preset
Append a serialized effect preset (id, name, stack with effects/keyframes/fps) to visual clips. Validates catalog parameters, remaps effect IDs and converts animation timing.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Target clips |
preset_json |
string | yes | Effect preset JSON |
paste_attributes
Copy selected groups from a source clip to targets. Replaces those settings/keyframes; effects append by default. Keeps timing/source offsets and respects locks.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
source_clip_id |
string | yes | Source clip |
clip_ids |
string[] | yes | Target clips |
groups |
"transform" | "color" | "audio" | "effects"[] | yes | Which attributes to paste: transform, color, audio, effects |
replace_effects |
boolean | Replace target effect stacks |
set_transition
Put a transition on the cut after a clip (the next clip must touch it). type 'none' removes it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The clip before the cut |
type |
"dissolve" | "dipBlack" | "dipWhite" | "wipeLeft" | "wipeRight" | "wipeUp" | "wipeDown" | "circleOpen" | "clockWipe" | "slideLeft" | "slideRight" | "pushLeft" | "pushUp" | "zoomIn" | "zoomOut" | "spin" | "blur" | "pixelate" | "glitch" | "flash" | "whipLeft" | "whipRight" | "zoomPunch" | "spinBlur" | "none" | yes | Transition |
duration |
number | Seconds (0.05..) |
Example:
{"name":"set_transition","arguments":{"clip_id":"c_123","type":"dissolve","duration":0.5}}
set_filter
Apply a color look (filter) to a visual or adjustment clip at an intensity 0..1. type 'none' removes it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Clip id |
type |
"vivid" | "bright" | "warm" | "cool" | "golden" | "teal" | "film" | "vintage" | "matte" | "cinematic" | "moody" | "dreamy" | "neon" | "forest" | "mono" | "noir" | "silver" | "none" | yes | Filter (look) |
intensity |
number | 0..1 (0..1) |
Example:
{"name":"set_filter","arguments":{"clip_id":"c_123","type":"teal","intensity":0.8}}
auto_adjust
Correct a video or picture clip's exposure, white balance and levels from its frame at the playhead (CapCut's Auto adjust), setting its Basic adjustment sliders.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or image clip |
set_hsl
Adjust one of CapCut's eight HSL color ranges on a visual or adjustment clip; only pixels of that color change. hue turns the range up to 30° toward its neighbors, saturation -100 makes it gray, lightness darkens or lightens it; each -100..100, omitted values stay. band 'all' sets every range. reset=true zeroes the band(s) first. Animate with add_keyframe on adjust.hsl..hue|saturation|lightness. Hue -100..100 is a turn of -30..30 degrees, so -30 is only 9 degrees: use 60 to 100 for a shift that shows.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Visual or adjustment clip id |
band |
"red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "purple" | "magenta" | "all" | yes | Color range |
hue |
number | -100..100 (-100..100) | |
saturation |
number | -100..100 (-100..100) | |
lightness |
number | -100..100 (-100..100) | |
reset |
boolean | Zero the band(s) before applying the values given |
set_curve
Set a tone curve on a visual or adjustment clip. channel luma maps R, G and B alike (Lumetri's white curve); r, g, b are per channel. points are [input, output] pairs in 0..1, joined by a monotone cubic that never overshoots; include [0,0] and [1,1] to pin the ends. Omit points or pass [] to reset the channel. Gentle S-curve: [[0,0],[0.25,0.2],[0.75,0.8],[1,1]].
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Visual or adjustment clip id |
channel |
"luma" | "r" | "g" | "b" | yes | Which curve |
points |
number[][] | [input, output] pairs, each 0..1 |
set_color_wheel
Resolve-style color wheels on a visual or adjustment clip: lift (shadows; white stays put), gamma (midtones), gain (highlights; black stays put), offset (everything). y is the wheel's luminance ring; r/g/b its color balance; each -100..100. Or give hue (degrees: 0 red, 60 yellow, 120 green, 180 cyan, 240 blue, 300 magenta) and amount (0..100) to push the balance toward a color, e.g. lift hue 200 amount 12 for teal shadows. reset=true returns the wheel to neutral first. Animate with add_keyframe on adjust.wheels..r|g|b|y.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Visual or adjustment clip id |
wheel |
"lift" | "gamma" | "gain" | "offset" | yes | Which wheel |
r |
number | Red balance, -100..100 (-100..100) | |
g |
number | Green balance, -100..100 (-100..100) | |
b |
number | Blue balance, -100..100 (-100..100) | |
y |
number | Luminance, -100..100 (-100..100) | |
hue |
number | Push the balance toward this hue, degrees (0..360) | |
amount |
number | How far toward hue, 0..100 (default 20) (0..100) | |
reset |
boolean | Neutral before applying the values given |
apply_lut
Apply a .cube LUT to a visual or adjustment clip, mixed in at intensity 0..1 (default 1). lut_id is from the project's luts (describeProject lists them; files are imported with window.videoEditor.importLuts or by dropping a .cube on the app). lut_id 'none' removes it. Animate with add_keyframe on adjust.lut.intensity.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Visual or adjustment clip id |
lut_id |
string | yes | LUT id, or 'none' |
intensity |
number | 0..1 (0..1) |
set_colorize
Colorize a picture (Premiere's Tint): its brightness drawn in one color, so white becomes the color, black stays black and grays become darker shades of it, mixed over the picture by amount 0..1. This is how a white sprite, glow, light leak or logo becomes amber or a brand color: temperature cannot warm pure white, which is already at the top of every channel. On video, image, title, shape and compound clips, and on an adjustment clip (everything under it). color: CSS (default '#ffb347', amber); amount default 1 (0.3-0.5 tints footage and keeps some of its own color). It runs first in the clip's color, so its exposure, temperature, filter and LUT act on the colorized picture. Values left out keep theirs. Animate with add_keyframe on adjust.colorize.amount (a white flash warming to amber); a keyframed amount gets a key at at instead. off: true removes it and its keyframes. Example: {"clip_ids": ["c_7"], "color": "#ffb347"}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Visual or adjustment clips |
color |
string | CSS color white becomes, e.g. #ffb347 amber; its alpha multiplies amount | |
amount |
number | 0..1, how much of the colorized picture shows over the original (default 1) (0..1) | |
off |
boolean | Remove the colorize and its keyframes | |
at |
number | Timeline seconds, for a keyframed amount |
Example:
{"name":"set_colorize","arguments":{"clip_ids":["c_7"],"color":"#ffb347","amount":1}}
Sound
Voice, music, effects and the mix.
add_voiceover
Narrate a script, directed: every line is read on this computer (Kokoro), whole, and placed after the one before at its real length on a new Voiceover track, with a caption for each timed to its voice (or, with caption_words 1-3, captions a few words at a time, each word timed to the voice for the spoken-word highlight: caption_clips per line), as one undo step with its speech files. Pace: each line lands on its words a second over its speech (first word to last; the reader's pauses at commas and sentence ends in, its pause marks out, so a beat written into a line never makes its words faster): read_as narration (2.3-2.8, the default), ad (2.6-3.1) or social (2.8-3.4), moved half a word down by pace slow (and then never read faster than speed 1) or up by fast, or words_per_second [lowest, highest], per call or per line, held as given whatever the pace, except that a band never rushes long words: one whose top would say the line faster than 5 syllables a second moves down until it does not (seven syllables in three words read 1.64-2.14, not 2.3-2.8), and pace_note says so; with no speed given, each line is read, measured and read again at the speed that lands it (up to three reads; the audio is never stretched). A speed given is kept as given. Direct each line: a voice of its own (several speakers in one call), at (a timeline time, such as a beat or a cut; later lines follow it), pause_after (seconds before the next line; by default the line's ending sets it: 0.4 after a sentence, 0.1 after a comma, 0.6 after an ellipsis, 0.8 after a paragraph), pace slow or fast. Pauses inside a line are written in it: [pause] (0.5 s), [pause 0.4], [pause 300ms], [beat] (1 s) or ; the voice's own pause there is lengthened, so the sentence keeps its intonation, and the caption never shows the mark. A word cannot be marked for emphasis (the voice model has no such control; word stars are dropped, with a note): write it last in its phrase, where English puts the main stress, or [pause 0.2] before it. pronounce respells words for the voice only ({"MCP": "M C P", "OpenFrame": "Open Frame"}); captions keep the words as written. The natural finish (default) makes it sit like a recorded read: pauses at commas and sentence ends a reader's length, a low cut and a little presence, every line at one loudness, room tone under the pauses, faded edges, the voice compressor and a touch of room. Answers each line's start, end, seconds, voice, speed, words_per_second (over its speech) and target_words_per_second, and clip ids: cut the footage to them, then review_voiceover to hear it back. Voices: af_heart, af_bella, af_nicole, bf_emma, am_michael, am_fenrir, am_puck, af_aoede, af_kore, af_sarah, bm_george, bm_fable, bf_isabella. The first use downloads the speech model (about 325 MB at quality best); a read takes about a second with WebGPU, several on a CPU.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
lines |
{ text, caption, at, voice, speed, words_per_second, read_as, pause_after, paragraph, pace }[] | yes | The script, one sentence or two per line (a breath each) |
lines[].text |
string | yes | Words to say and show, with pause marks: [pause], [pause 0.4], [pause 300ms], [beat], |
lines[].caption |
string | What the caption shows instead, when it should differ from the words spoken (20% off for 'twenty percent off', a URL) | |
lines[].at |
number | Timeline seconds this line starts (a beat, a cut); later lines follow it; default after the line before. A line the one before runs past starts where that one ends, and a note says so (0..) | |
lines[].voice |
string | This line's voice (another speaker); default the call's | |
lines[].speed |
number | This line's speed, kept as given; default the call's, or chosen to land on its words a second (0.5..2) | |
lines[].words_per_second |
number[] | This line's [lowest, highest] words a second; default the call's | |
lines[].read_as |
"narration" | "ad" | "social" | This line's kind of read (narration, ad or social), whose band it lands on: another speaker's brisk radio call among narration; default the call's | |
lines[].pause_after |
number | Seconds of silence before the next line (overrides gap and the line's ending) (0..10) | |
lines[].paragraph |
boolean | This line ends a paragraph: 0.8 s before the next | |
lines[].pace |
"slow" | "normal" | "fast" | The pace the line is meant to have, kept with it: slow (an end-card tagline, a dramatic beat) or fast moves its kind's words a second half a word down or up, for the read and the review, and a slow line is read no faster than speed 1; a words_per_second given wins over it (default normal) | |
voice |
string | Voice id, or a blend of voices' styles ("af_heart:60,af_bella:40"); default af_heart, the most natural | |
read_as |
"narration" | "ad" | "social" | The kind of read, whose words a second lines land on: narration 2.3-2.8, ad 2.6-3.1, social 2.8-3.4 (default narration) | |
words_per_second |
number[] | [lowest, highest] words a second over each line's speech, first word to last, its pause marks out (narration [2.3, 2.8]); held exactly as given, whatever the pace; the speed is chosen to land on it | |
speed |
number | Every line's speed, kept as given (1 is the model's own pace, about 4.6 syllables a second); default: chosen per line to land on its words a second (0.5..2) | |
pronounce |
object | Words respelled for the voice only (whole words, case as written), each to the text the voice reads: {"MCP": "M C P", "OpenFrame": "Open Frame"} | |
start |
number | Seconds the first line starts, default 0 (0..) | |
gap |
number | Seconds between every pair of lines, instead of each line's ending choosing (0..) | |
quality |
"best" | "draft" | best: full-precision weights (default; on WebGPU, or on the CPU about as fast as draft); draft: 8-bit weights, a quarter of the download | |
finish |
"natural" | "clean" | "raw" | natural (default): a recorded read's pauses, EQ, one loudness, room tone, the voice compressor and a touch of room; clean: one loudness and faded edges, nothing added; raw: the model's read, trimmed | |
captions |
boolean | A caption per line (default true) | |
caption_words |
integer | Caption each line this many words at a time (1-3; 2 or 3 for a short's pop captions) instead of one caption a line: each word timed to the voice (heard by Whisper, its start fitted to the voice's own start and pauses), its times kept for the spoken-word highlight (caption_style.highlight); a retake captions its line the same way (1..3) | |
enhance |
boolean | Even out each voice with the Enhance voice compressor (default true; finish raw has none) | |
caption_style |
{ font, size, color, bold, weight, italic, align, highlight, case, background, stroke, stroke2, shadow, glow, gradient, accent, letterSpacing, lineHeight, maxWidth } | The captions' look, as a title's style takes it; its fields are add_text's style (help add_text) | |
caption_y |
number | Captions' vertical offset from center, canvas fraction (default 0.36, or 0.05 on a tall canvas, above the apps' captions and buttons) |
Example:
{"name":"add_voiceover","arguments":{"lines":[{"text":"Meet OpenFrame. [pause 0.4] The video editor that runs on your own computer."},{"text":"Every edit is a tool an agent can call,","voice":"am_michael"},{"text":"so a whole film can be made by asking.","voice":"am_michael","pause_after":0.8},{"text":"Free. [beat] Open source. Yours.","at":12,"caption":"Free, open source, yours.","pace":"slow"}],"voice":"af_heart","read_as":"ad","pronounce":{"OpenFrame":"Open Frame"}}}
review_voiceover
Listen back to voiceover lines on this computer: Whisper hears each voice clip and compares it with its script word by word (a misread name or a skipped word shows, with where), and each line is measured: its pace (words and syllables a second over its speech, first word to last with its pause marks out, as it was read: pause_marks_out says how many seconds; and over its clip) against the words a second it was read to (narration 2.3-2.8 unless add_voiceover was given read_as or words_per_second; pace slow or fast moves a kind's band, never a words_per_second given; any band moves down for long words, as the speed was chosen, so its top says at most 5 syllables a second), the silence before and after it and its longest pause, its loudness and true peak as mixed, its pitch (median and range in semitones; under 3 sounds flat) and how far it sits over the music under it in the line window (this line alone, its first word to its last; at least 8, 10-15 for a promo). Each line ends with a verdict: ok when nothing fails, otherwise what to fix, with fixes: the tool and arguments for each problem (retake_voiceover with a pronunciation or words_per_second, set_param volume, duck_under_speech min_line_lu, trim_clip). advice holds notes that do not fail the line: a pace within a tenth of its band, a speed already chosen that the voice could not bring nearer, short words drawn out. A line that differs from its script is heard again by a larger Whisper (small, downloaded once, about 250 MB) before the voice is blamed, a word heard that is not in the script too (Whisper hears "you" in a breath): each difference says likely recognizer (heard right the second time: no retake, a note says so) or voice, and heard_again has what it heard; minor marks a short word a letter off, which passes. match is the share of words heard as written, a word heard that is not in the script counting against it, so it is under 1 whenever a difference is listed. A misread word gets a respelling as it sounds (does as duz, home as hohm, MCP as M C P); one already respelled, or with nothing that reads differently, gets a slower read and the advice to reword. The summary counts lines ok, to fix and with advice; gives the lines' words a second (lowest, median, highest), the script's words a second with the gaps between lines in (lower than the lines'), the lines' margins over the music and the loudness spread across lines. Lines short of their margin over a music track get one fix in summary.fixes: duck_under_speech on that track (track_id, every piece of the song as one bed), each line pointing at it. detail summary (the default) gives each line its verdict (ok, or to fix: the problems are in its fixes), fixes, advice, words a second against its band, loudness and margin over the music, and what was heard only where it differs from the script; detail full adds every measure (syllables, the clip window, silences, true peak, pitch). Nothing is edited. The first use downloads Whisper (about 80 MB); each line takes a second or two.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | Voice clips to hear (add_voiceover's voice_clip ids); default every voiceover line | |
music |
boolean | Measure each line's margin over the music tracks under it (default true) | |
recheck |
boolean | Hear a line with a difference again with a larger Whisper model (small, 249 MB downloaded once), to say whether the recognizer or the voice is wrong (default true) | |
detail |
"summary" | "full" | summary (default): each line's verdict and what decides it; full: every measure of every line |
Example:
{"name":"review_voiceover","arguments":{}}
retake_voiceover
Read one voiceover line again and put it in the line's place, as one undo step: new text (with pause marks), a voice, words_per_second (held as given over any pace the line keeps, moved down only so long words are not said faster than 5 syllables a second: pace_note says so) or read_as (the speed is then chosen by reading and measuring, as add_voiceover does, pause marks out of the pace), a speed (kept as given) or pronunciations (the pronounce fix review_voiceover suggests). With none of these, a line whose speed was chosen is landed on its words a second again, and a line read at a speed given keeps it. The voice model reads the same input the same way every time, so change something. The clip keeps its id, start and settings; its caption takes the new words and length (a line captioned a few words at a time, caption_words, is captioned so again, timed to the new take: caption_clips); the old take leaves the media library. A longer take must fit before the next line unless ripple is true, which moves the lines and captions after it by the difference (the picture stays). Answers the line's new start, end, seconds, voice, speed, words_per_second (over its speech) and target_words_per_second; when the speed was chosen, speed_chosen is true and a note says how many reads it took. Music that duck_under_speech dipped under the line (or under the lines a ripple moved) is dipped again under the new take as it was asked, in the same undo step: ducking lists each, with the voice's margin over it now; music whose volume keys were edited by hand since is left alone and a note says so.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The voice clip (add_voiceover's voice_clip) |
text |
string | New words, with pause marks; default the line's own | |
caption |
string | What the caption shows, when it should differ from the words; default the new text as written | |
voice |
string | Voice id (list_catalog section voices), or a blend ("af_heart:60,af_bella:40"); default af_heart | |
read_as |
"narration" | "ad" | "social" | The kind of read, whose words a second lines land on: narration 2.3-2.8, ad 2.6-3.1, social 2.8-3.4 (default narration) | |
words_per_second |
number[] | [lowest, highest] words a second over the line's speech; default the line's own | |
speed |
number | Kept as given; default the line's own speed, or chosen again when it was chosen (0.5..2) | |
pronounce |
object | Words respelled for the voice, replacing the line's own map ({} clears it) | |
quality |
"best" | "draft" | best: full-precision weights (default; on WebGPU, or on the CPU about as fast as draft); draft: 8-bit weights, a quarter of the download | |
finish |
"natural" | "clean" | "raw" | natural (default): a recorded read's pauses, EQ, one loudness, room tone, the voice compressor and a touch of room; clean: one loudness and faded edges, nothing added; raw: the model's read, trimmed | |
pace |
"slow" | "normal" | "fast" | The pace the line is meant to have; default the line's own | |
ripple |
boolean | Move the lines and captions after it by the change in length (default false) | |
caption_words |
integer | Caption the line this many words at a time (1-3), each word timed to the new take, or 0 for one caption for the line; default as the line was captioned (0..3) |
Example:
{"name":"retake_voiceover","arguments":{"clip_id":"c_vo2","pronounce":{"MCP":"M C P"},"words_per_second":[2.3,2.8],"ripple":true}}
text_to_speech
Read a title aloud in a voice made on this computer (Kokoro), placing the voice on an audio track at the title's start, as one undo step with its speech file. With no speed given it lands on a narrator's words a second (2.3-2.8; pace slow or fast moves it half a word), its speed chosen by reading and measuring. Voices (list_catalog section voices describes each): af_heart, af_bella, af_nicole, bf_emma, am_michael, am_fenrir, am_puck, af_aoede, af_kore, af_sarah, bm_george, bm_fable, bf_isabella. The first use downloads the speech model.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The title (text clip) |
voice |
string | Voice id (list_catalog section voices), or a blend ("af_heart:60,af_bella:40"); default af_heart | |
pace |
"slow" | "normal" | "fast" | The pace the line is meant to have, kept with it: slow (an end-card tagline, a dramatic beat) or fast moves its kind's words a second half a word down or up, for the read and the review, and a slow line is read no faster than speed 1; a words_per_second given wins over it (default normal) | |
speed |
number | 1 is the model's own pace; kept as given. Default: chosen per line to land on its words a second (Heart narrates at 0.6-0.9) (0.5..2) | |
pronounce |
object | Words respelled for the voice only (whole words, case as written), each to the text the voice reads: {"MCP": "M C P", "OpenFrame": "Open Frame"} | |
quality |
"best" | "draft" | best: full-precision weights (default; on WebGPU, or on the CPU about as fast as draft); draft: 8-bit weights, a quarter of the download | |
finish |
"natural" | "clean" | "raw" | natural (default): a recorded read's pauses, EQ, one loudness, room tone, the voice compressor and a touch of room; clean: one loudness and faded edges, nothing added; raw: the model's read, trimmed |
add_sound_effect
Add built-in sound effects, made on this computer: whoosh (Whoosh, 0.8 s), swish (Swish, 0.35 s), pop (Pop, 0.15 s), click (Click, 0.06 s), ding (Ding, 1.4 s), success (Success, 0.5 s), rise (Riser, 2 s), impact (Impact, 1.2 s), subDrop (Sub drop, 1.8 s), braam (Braam, 3 s), reverseCymbal (Reverse cymbal, 2 s), glitch (Glitch, 0.5 s), shutter (Camera shutter, 0.25 s), swooshUp (Swoosh up, 0.9 s), boom (Boom, 1.5 s). One lands at start (default the playhead), or so that it ENDS at end_at: a riser or reverse cymbal then peaks exactly there (put end_at on the drop or a cut); a hit, boom or sub drop goes at the beat with start. They land on the effects stem: an SFX track (made the first time, reused after; overlapping sounds get a second SFX track), never a gap on the voiceover or the music; track_id puts them on a track of your own. sounds places many at once, as one undo step: a whoosh on every cut of a montage in one call (it cannot go in batch). Each file is made once per project and reused. add_ambience lays continuous natural sound (wind, water, rain, city, a rocket's rumble, room tone). Answers the new clip ids.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
"whoosh" | "swish" | "pop" | "click" | "ding" | "success" | "rise" | "impact" | "subDrop" | "braam" | "reverseCymbal" | "glitch" | "shutter" | "swooshUp" | "boom" | Which effect (list_catalog section sound_effects has their lengths) | |
start |
number | Timeline seconds the sound starts (0..) | |
end_at |
number | Timeline seconds the sound ends instead (start = end_at - its length) (0..) | |
volume |
number | Gain, 1 as made (0.5 is about -6 dB, 0.25 -12 dB); default 1 (0..4) | |
track_id |
string | An audio track to put them on (default the SFX track); where a sound would overlap one there, a new track of its role just below | |
sounds |
{ name, start, end_at, volume, track_id }[] | Many sounds in one call instead of name: each with its own name, start or end_at, volume and track_id | |
sounds[].name |
"whoosh" | "swish" | "pop" | "click" | "ding" | "success" | "rise" | "impact" | "subDrop" | "braam" | "reverseCymbal" | "glitch" | "shutter" | "swooshUp" | "boom" | yes | Which effect |
sounds[].start |
number | Timeline seconds it starts (default the playhead) (0..) | |
sounds[].end_at |
number | Timeline seconds it ends instead (0..) | |
sounds[].volume |
number | Gain, default 1 (0..4) | |
sounds[].track_id |
string | Its audio track; default the call's track_id, else the SFX track |
Example:
{"name":"add_sound_effect","arguments":{"sounds":[{"name":"whoosh","start":3.9},{"name":"whoosh","start":7.9,"volume":0.6},{"name":"rise","end_at":12},{"name":"impact","start":12}]}}
add_ambience
Lay continuous natural sound under the pictures, made on this computer (no recordings, nothing downloaded): wind (open-air wind in gusts, a low buffeting under it); stream (a brook babbling over stones, close); waterfall (a waterfall's steady roar, close enough to feel); surf (waves breaking on a beach and washing back, a distant roar between); rain (steady rain on leaves and ground, drops near and far); rocket (a rocket's launch heard from a distance: a deep roar with its crackle); city (a city street: cars passing near and far over the city's low hum); roomTone (a quiet room's air: ventilation and the room itself, for under and between lines). It runs from start (default the playhead) to end, or for length seconds (default to the end of the timeline), faded in and out (fade_in 1 s and fade_out 1.5 s by default), on the Ambience track (made the first time, reused after; never the voiceover, the music or the effects). Each bed moves slowly and at random as a recording does (gusts, waves arriving every 7 to 13 s, cars passing near and far) and its file loops without a seam; a bed past 300 s repeats it. Made at a level that sits under a voice with volume 1 (about -27 LUFS; the rocket -21, room tone -46): volume 0.5 is 6 dB quieter. The same name and seed reuse the file; another seed is another take. preview_audio with solo ambience hears it. Answers the clip ids, the file's asset id and the track. It cannot go in batch.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
"wind" | "stream" | "waterfall" | "surf" | "rain" | "rocket" | "city" | "roomTone" | yes | Which bed (list_catalog section ambience describes each) |
start |
number | Timeline seconds it starts; default the playhead (0..) | |
end |
number | Timeline seconds it ends; default the end of the timeline (0..) | |
length |
number | Seconds it lasts, instead of end (0.5..) | |
fade_in |
number | Seconds it fades in over; default 1 (0..) | |
fade_out |
number | Seconds it fades out over; default 1.5 (0..) | |
volume |
number | Gain, 1 as made (0.5 is about -6 dB); default 1 (0..4) | |
seed |
integer | Another take of the same bed (default 1) (1..) | |
track_id |
string | An audio track to put it on; default the Ambience track |
Example:
{"name":"add_ambience","arguments":{"name":"waterfall","start":22,"end":34,"fade_in":1.5,"fade_out":2,"volume":0.7}}
set_fades
Fade a clip's sound in and out, in SECONDS (a music bed: fade_in 1, fade_out 2). Works on audio clips and on video clips' own sound; a value left out keeps its fade, 0 removes it. Each fade is at most half the clip. Pictures fade with set_animation (slot in or out, type fade).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Audio or video clips |
fade_in |
number | Seconds (0..) | |
fade_out |
number | Seconds (0..) |
Example:
{"name":"set_fades","arguments":{"clip_ids":["c_123"],"fade_in":1,"fade_out":2}}
set_audio_filter
Filter a video or audio clip's sound, 24 dB per octave. type lowpass keeps the lows (muffled, as through a wall or from the next room); highpass keeps the highs (thin, as on a phone or a small radio). cutoff in Hz, 20..20000 (a new filter starts at 500 Hz low-pass, 300 Hz high-pass); without sweep it is fixed (its keyframes go). resonance in dB, 0..24 (default 0, a smooth slope; 6..12 adds the whistling peak of a synth sweep). sweep moves the cutoff from cutoff at sweep.start to sweep.to at sweep.end (timeline seconds), on a log scale, holding before and after; it replaces the cutoff's keyframes. The trailer move "muffled build, open on the drop" at 5 s: {type: "lowpass", cutoff: 400, sweep: {to: 20000, start: 4.5, end: 5}}. add_keyframe on lowpass.cutoff or highpass.cutoff adds more points. off: true removes that filter and its keyframes. A filter takes loudness out with what it cuts: a radio band-pass (highpass 380, lowpass 3400) took 3-3.6 LU off a voice. keep_loudness: true measures the clip before and after and makes up the difference after the filter (its makeup, dB), so a filtered voice line stays as loud as the others; it measures on this computer, so it runs on its own, not in a batch. makeup sets that gain by hand. Filtering a voice line without either answers a note.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or audio clips |
type |
"lowpass" | "highpass" | yes | Which filter |
cutoff |
number | Hz (at sweep.start when sweeping) (20..20000) | |
resonance |
number | Peak at the cutoff, dB; 0 = smooth (0..24) | |
sweep |
{ to, start, end, ease } | Move the cutoff from cutoff to to between two times (a filter sweep): keyframes, so it plays the same in preview and export |
|
sweep.to |
number | yes | Cutoff at the end of the sweep, Hz (20..20000) |
sweep.start |
number | yes | Timeline seconds the sweep begins (0..) |
sweep.end |
number | yes | Timeline seconds it arrives (0..) |
sweep.ease |
"linear" | "hold" | "easeIn" | "easeOut" | "easeInOut" | "smooth" | "fastIn" | "overshoot" | "anticipate" | "spring" | "elastic" | "bounce" | Curve of the sweep (default linear: the same octaves each second); the keyframe tools' eases | |
off |
boolean | Remove this filter | |
makeup |
number | Gain after the filter, dB, to make up what it takes out (absent: 0) (-24..24) | |
keep_loudness |
boolean | Measure the clip before and after and set makeup so it stays as loud (a fixed filter only; not in a batch) |
Example:
{"name":"set_audio_filter","arguments":{"clip_ids":["c_3"],"type":"lowpass","cutoff":400,"sweep":{"to":18000,"start":6,"end":8.4,"ease":"easeIn"}}}
set_reverb
Put a video or audio clip's sound in a room (a convolution reverb). preset room (small and close), hall (a concert hall; what a new reverb starts as) or cathedral (huge and long, for trailers and epic edits). size 0..1 (pre-delay 5 to 50 ms, slower build), decay: seconds for the tail to fall 60 dB (0.1..10), mix: wet share 0..1 at equal power, so the clip stays as loud (0.2 subtle, 0.5 drenched). It comes after the clip's volume and its tail rings on past the clip's end, so a voice's last word hangs in the room. Values left out keep theirs, or the preset's. A reverb throw on one word: add_keyframe on reverb.mix. off: true removes it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or audio clips |
preset |
"room" | "hall" | "cathedral" | Starting point | |
size |
number | Room size, 0..1 (0..1) | |
decay |
number | Seconds to fall 60 dB (RT60) (0.1..10) | |
mix |
number | Wet share, 0..1 (0..1) | |
off |
boolean | Remove the reverb |
Example:
{"name":"set_reverb","arguments":{"clip_ids":["c_9"],"preset":"hall","mix":0.25}}
set_echo
Repeat a video or audio clip's sound (a delay). time: seconds between repeats, 0.01..2 (0.08-0.15 a slapback, 0.3-0.5 an echo; on music 60/BPM lands on the beat). feedback: each repeat's level against the one before, 0..0.9 (0.4 gives a few repeats, 0.8 a long trail). mix: wet share 0..1 at equal power. A new echo is 0.35 s, 0.4, 0.3; values left out keep theirs. It comes after the clip's volume, so the repeats ring on past the clip's end: an echo on a short hit or sound effect trails off after it. Echo one word only: add_keyframe on echo.mix. off: true removes it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or audio clips |
time |
number | Seconds between repeats (0.01..2) | |
feedback |
number | Each repeat's level against the one before (0..0.9) | |
mix |
number | Wet share, 0..1 (0..1) | |
off |
boolean | Remove the echo |
Example:
{"name":"set_echo","arguments":{"clip_ids":["c_9"],"time":0.375,"feedback":0.4,"mix":0.3}}
set_tape_stop
Make a video or audio clip's sound slow to a stop over its last duration seconds, its pitch diving with it, as a turntable or tape machine losing power: the classic ending, or a cut into silence before a drop. duration 0.05..10 seconds (default 1; 0.5 a quick stop, 2-3 a long wind-down; never more than the clip). Only the sound stops; a video's picture plays on. Splitting the clip keeps it on the part with the end. off: true removes it.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or audio clips |
duration |
number | Seconds the stop takes (0.05..10) | |
off |
boolean | Remove the tape stop |
Example:
{"name":"set_tape_stop","arguments":{"clip_ids":["c_3"],"duration":0.8}}
set_compressor
Even out a clip's sound with a compressor after its EQ (CapCut's Enhance voice, Premiere's Dynamics): preset voice (speech; the usual choice), gentle, strong, or off. threshold (dBFS), ratio, knee (dB), attack and release (seconds) and makeup (dB) adjust it. On a compound clip it acts on the mix of everything inside, as a bus does: one compressor over a voice and its music, after the compound clip's EQ and before its volume and fades. normalize_loudness measures through it, and export loudness reaches its target with far less limiting.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video, audio or compound clips |
preset |
"voice" | "gentle" | "strong" | "off" | Starting point | |
threshold |
number | dBFS where compression starts (-60..0) | |
ratio |
number | dB in over the threshold per dB out (1..20) | |
knee |
number | Soft knee, dB (0..40) | |
attack |
number | Seconds (0..1) | |
release |
number | Seconds (0..1) | |
makeup |
number | Extra dB after the compressor's own makeup gain (presets: 0) (0..24) |
set_channel_fill
Play one channel of a video's or audio clip's sound on both sides (Premiere's Fill Left / Fill Right), e.g. a lav mic recorded on one channel. both = as recorded.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or audio clips |
fill |
"both" | "left" | "right" | yes | Which channel fills both |
set_crossfade
Crossfade an audio clip into the next clip on its track, which starts where it ends (two pieces of a song, two songs, two takes of a bed): at equal power over seconds, ending at the cut, the next clip's sound rising over the seconds before its start from its file before its in-point (it needs that much), this one falling over its last. 0 makes it a butt cut again. A butt cut ramps each side over 4 ms to stop a click, which leaves a few milliseconds of near-silence at the join; preview_audio's edges measure it. Both clips play at their own speed. Example: {"clip_id": "c_3", "seconds": 0.03}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The audio clip before the join |
seconds |
number | yes | Seconds of crossfade (0 removes it; at most 2) (0..2) |
Example:
{"name":"set_crossfade","arguments":{"clip_id":"c_3","seconds":0.03}}
normalize_loudness
Measure clips' loudness (EBU R128) and set their volume to reach a target without their true peak passing the ceiling (-1 dBTP; -2 for ATSC). -14 LUFS suits social video, -16 podcasts, -23 European broadcast, -24 US TV. spec names a delivery spec instead (its target and ceiling). A ducked clip keeps its duck: its volume keys scale so the loudest is the new level. Each clip is measured alone: measure_loudness checks the mix.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_ids |
string[] | yes | Video or audio clip ids |
target |
number | LUFS, default -14 (-40..-5) | |
spec |
"online" | "ebu_r128" | "atsc_a85" | online (-14 LUFS, -1 dBTP), ebu_r128 (-23 LUFS, -1 dBTP) or atsc_a85 (-24 LKFS, -2 dBTP); overrides target |
duck_under_speech
Lower music wherever the other clips with sound under it (inside compound clips too) have speech, as editable volume keyframes: one clip (clip_id), or every clip of a music track as one bed (track_id: a song cut into pieces is ducked in one call, a line across a cut dipping on both sides). A voice's balance over music depends on the window it is measured in, and the answer names it: speech (while the voice speaks, every line from its first word to its last, together) and line (each line alone); measure_loudness by_track also gives program (the whole stretch, music between lines included, which reads lower). voice_above_music_lu aims the voice that many LU over the music in the speech window; min_line_lu holds every line to at least that many LU over the music, each line's dip as deep as it needs (with voice_above_music_lu, or depth, as the usual dip); max_line_lu holds every line to at most that many, a line over a quiet passage dipping less (never raised over its volume), so a quiet intro or a last hit is not emptied: by default 15 LU (or 3 over voice_above_music_lu or min_line_lu, when higher) whenever any of the three is given, and never under voice_above_music_lu (lines each under it cannot sit over it together); a line held under the ceiling moves the others so the lines together stay on voice_above_music_lu. All are measured on the rendered stems and met by measuring again; balance answers the speech window's numbers, target and met, and per_line the lines' lowest, median and highest margins, the minimum and ceiling held to, and each line with its own dip. Plain depth lowers by that many dB without measuring. Between lines: a pause shorter than min_gap (default 1.1 s) stays down so the music does not pump, and so does the music before a first line or after a last one that close to its start or end (no blip); a longer pause comes back up over release (default 0.4 s) and dips again 0.1 s before the next line. open_at brings the music up on chosen moments whatever their pause: timeline seconds, or marker labels ("Drop": every marker so labeled, as analyze_music and set_music_grid mark a song's drops), so a drop between lines plays at the music's level; a moment inside a line stays down under the voice. The answer lists the pauses held down (held, seconds), each open_at moment with where the music is up around it (up) or the line it falls in (inside_line), and notes a drop marker the music stays down through. Volume keys made by hand (added or changed since the last duck: a lift on a drop, a hold before a hit) are kept wherever no line speaks, in place of the duck's own in that pause, the duck's level held at the lines either side; one under a line is replaced, and hand_keys lists both. The music keeps how it was ducked: a voice line that changes later (retake_voiceover) dips it again the same way, and an edit that moves a line under it, or moves the music and its dips (cut_music, move_clip), says so.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | The music clip (or give track_id) | |
track_id |
string | A music track: every clip with sound on it ducked as one bed, instead of clip_id | |
voice_above_music_lu |
number | LU the voice sits over the music while it speaks (all lines together; 10-15 for a promo); measured and met, overrides depth (0..30) | |
min_line_lu |
number | LU every line sits over the music at least (each line alone, its first word to its last); a line short of it dips deeper, measured and met (0..30) | |
max_line_lu |
number | LU every line sits over the music at most; a line past it dips less, measured and met (default 15, or 3 over voice_above_music_lu or min_line_lu when higher) (0..30) | |
depth |
number | dB to lower, default 12; with min_line_lu alone, the least dip under every line (default none) (1..40) | |
open_at |
any[] | Moments the music comes up at between lines: timeline seconds, or marker labels ("Drop": every marker so labeled); default none | |
min_gap |
number | Seconds a pause between lines must last for the music to come up (default 1.1: shorter pauses stay down, so the music does not pump) (0.2..10) | |
release |
number | Seconds the music takes to come back up after a line (default 0.4) (0.05..3) |
Example:
{"name":"duck_under_speech","arguments":{"track_id":"t_music","voice_above_music_lu":12,"min_line_lu":10}}
reduce_noise
Take steady background noise (hiss, hum, fans, room tone) out of a clip's sound on this computer; the clip then plays a cleaned copy (a video's sound is extracted to an audio clip first).
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
strength |
number | 0.5 gentle, 0.75 default, 1 strong (0..1) |
sync_audio
Line up a separately recorded sound (a lav, a recorder) with the camera clip of the same take by matching their waveforms on this computer (CapCut's Sync video and audio, Resolve's Auto Sync Audio), then move the audio clip into sync, exact to the sample (one undo step; sound before 0 is trimmed off its head). Answers the offset (audio source seconds minus video source seconds) and a confidence; refuses when the sounds do not clearly match. sync_to_clip places it by a known offset instead.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | The audio clip to move |
video_clip_id |
string | The camera clip; default the video with sound that plays with it longest | |
mute_camera |
boolean | Set the video's volume to 0 (default false) |
remove_silences
Find the pauses in a clip's speech and cut them out, closing the gaps (one undo step). Returns the silent source ranges and seconds saved.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
sensitivity |
number | 0..1, default 0.5 (0..1) | |
min_silence |
number | Shortest pause to cut, seconds (default 0.4) (0..) | |
padding |
number | Kept around speech, seconds (default 0.1) (0..) |
remove_filler_words
Transcribe a clip's speech on this machine (Whisper) and cut its filler words (um, hmm, ah) with the hesitation around each, closing the gaps (one undo step). Whisper does not transcribe "uh", so those stay. Returns the source ranges cut and the words heard.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
clip_id |
string | yes | Video or audio clip id |
model |
string | Whisper model id (default onnx-community/whisper-base.en_timestamped) |
Check and deliver
Look at the media and the result, check it against the rules, then export.
preview_media
Look inside imported media before using it: a video's own frames at source seconds (times), or count frames spread over the whole file (default 12), as one contact sheet; a still picture whole, labeled with its size. Use it to choose the in-points and moments worth using (a hero shot, a reaction, the action) and to see what a photo shows before framing it. file instead of asset_id looks inside a file without importing it: an exported video (the path export_video or export_versions answered) to see what was delivered, frame by frame as the file has it, not the timeline; the answer adds the file's length and size.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
asset_id |
string | A video or picture asset id from describe_project or import_media | |
file |
string | Instead of asset_id: a video or picture file to look inside without importing it, e.g. an exported file's path (desktop: an absolute path; the browser build: a URL) | |
times |
number[] | Source seconds, up to 24 | |
count |
integer | Frames spread over the file when times is not given; default 12 (2..24) | |
columns |
integer | Tiles a row, at most 8; default 4 (1..8) | |
tile_width |
integer | Default 320 (120..640) |
Example:
{"name":"preview_media","arguments":{"asset_id":"a_123","count":12}}
preview_frames
See several moments at once: one JPEG contact sheet of the frames at the given times, each stamped with its time. Check a whole edit in one call, then preview_frame a moment that needs a closer look.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
times |
number[] | yes | Seconds, up to 24 |
columns |
integer | Tiles a row, at most 8; default 4 (1..8) | |
tile_width |
integer | Width of each frame, default 320 (120..640) |
Example:
{"name":"preview_frames","arguments":{"times":[0.5,2,4,6,8,10,12,14]}}
preview_motion
See movement and timing: consecutive frames of a stretch of the timeline (default 8 a second, at most 36) as one contact sheet, plus, as text, the cuts on the main track and the markers (beats) in that stretch. Use it to check that cuts land on the beat, a move is smooth, or a transition reads; preview_frames is for whole-video overviews.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
start |
number | yes | Timeline seconds (0..) |
end |
number | Timeline seconds; default start + 2 (0..) | |
fps |
number | Frames a second, default 8 (1..30) | |
columns |
integer | Tiles a row, at most 8; default 6 (1..8) | |
tile_width |
integer | Default 240 (120..640) |
Example:
{"name":"preview_motion","arguments":{"start":7.5,"end":9.5,"fps":10}}
preview_frame
See the composited picture at a time: returns a PNG image of the frame exactly as it would export.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
at |
number | yes | Seconds (0..) |
max_width |
integer | Image width, default 640 (at most the canvas width) (64..1920) |
preview_audio
Hear the sound: a stretch of the mix, or one stem, rendered offline through the mixer export uses (every clip's volume, fades, ducking keys, EQ, compressor, filters, reverb and echo tails), returned as a short MP3 (an audio content block, 96 kbps stereo, at most 30 s a call) for a model that can listen, and as text the measurements for one that cannot: integrated loudness, the loudest 3 s and 400 ms, every second's loudness, the true peak, silent stretches, where the energy sits (below 250 Hz, to 4 kHz, above, and the octave slope; speech and most mixes slope -3 to -6 dB an octave), the stereo correlation and clipped samples; with solo mix also each stem's loudness (dialog, music, effects, ambience, by each track's role, and picture: what the video tracks play, their footage's own sound and any compound clip's mix), each its own integrated loudness where it sounds, so they do not add up to the mix's. edges measures, sample by sample, every place in the stretch where an audio clip meets the next on its track (a song's splice) and any clip start or end that is not clean: dip_db (a hole of a few milliseconds at the cut, as a butt cut's click guard leaves: -10 or deeper is heard as a tick), click_db (a sample step steeper than any around it and than the incoming file's own attack there, so a kick on the downbeat a cut lands on is no click), step_db (the level after against before; a join stepping 6 dB or more that the music makes there itself, on a new bar or hit, says so in step_note, and one inside a ring-out (the sound falling 6 dB or more over the second before) that neither file makes there is flagged at 6 dB, not 12), tail_cut_db (the clip's own file rings on that much louder than what follows: a tail cut dead) and cut_in_db (a clip coming in mid-sound), each with a verdict (clean, or what is wrong) and the fix (set_crossfade, set_fades); dropouts lists anywhere the sound falls to nothing for 3-100 ms inside sound (a lost packet, a hole). solo: mix (default), voice (the dialog tracks alone), music, effects or ambience; the picture's own sound plays only in mix. loudness hears it as export_video with that loudness delivers it (the whole mix is measured first, so it takes longer). audio false answers the measurements alone. Nothing is changed.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
start |
number | yes | Timeline seconds (0..) |
end |
number | Timeline seconds; default start + 10, at most start + 30 (0..) | |
solo |
"mix" | "voice" | "music" | "effects" | "ambience" | What to hear: the whole mix (default) or one stem by role | |
loudness |
number | Hear it as export_video with this loudness (LUFS) delivers it; default as mixed (-40..-5) | |
audio |
boolean | false: the measurements only, no MP3 (default true) |
Example:
{"name":"preview_audio","arguments":{"start":6,"end":14,"solo":"mix"}}
check_project
Check the edit against professional rules before exporting: text outside the 90% title-safe area or under the vertical apps' buttons; text too small for a phone (its share of the frame's height named, under 4.5% of the short side, 5% on a tall frame or kind social_short) or too short to read; text in low contrast with the picture behind it: every title is measured on the rendered picture at a few moments of its life with its box, shadow and outline counted, and a low one is named with its WCAG ratio, the time, and a box or outline that measures better there (contrast.lowest lists every title's ratio); a title that fades unevenly (measured at the middle of each fade: part of it gone while the rest is still seen, as a see-through box over a bright subject does), with the box or outline that fades as one there; captions over 20 characters a second, more than two fonts, a title its subject hides (text_behind_subject on a shape the subject now fills; rendered), flashes over 3 a second (seizure risk), black gaps on the main track, music that stops dead at the end (its last second measured, so a track that fades out in its own audio passes), a sound cut off before the end with no fade while it still sounds (a song or a bed trimmed mid-phrase, a line cut mid-word), a click, a hole or a sound cut off where one audio clip meets the next (each join measured on its own track as preview_audio's edges measure it, the first 24; click_at_join, sound_cut_at_join, the fix a crossfade), and by kind: an ad's end card, an epic edit's cuts on the beat (the Beat, Bar, Phrase and Drop markers, or the grid set_music_grid gave a song, where its clips play it; cuts counted where the viewer sees them, across tracks). ok is true when there are no errors; warnings and tips can remain beside it, and verdict says so in words. not_checked says what was not measured, and why. Every text rule sees the titles inside compound clips too (an end card built as one), where and as large as the compound clip draws them; such a finding names compound_clip_id. Each finding names the clip, the time and the tool that fixes it; the same finding on three clips or more (the same rule and fix) is given once, with count, every clip_id and every time (at), unless detail is full. Captions shown a few words at a time (runs of captions of up to three words, each within 0.3 s of the one before) are read as the line they make: its characters a second over the run, and each chunk up at least 0.25 s. Run it, fix, run it again.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
kind |
"ad" | "epic_edit" | "social_short" | "any" | What is being made; default any | |
detail |
"summary" | "full" | summary (default): alike findings given once with every clip; full: every finding on its own |
Example:
{"name":"check_project","arguments":{"kind":"ad"}}
measure_loudness
Measure the mix the way export renders it (48 kHz stereo, every clip's effects, tails and fades) and check it against a delivery spec before exporting. Answers integrated_lufs (BS.1770), loudness_range_lu (EBU Tech 3342 LRA) with range (the LRA against what kind usually spans: film 5-15 LU, ad 3-8, social 3-10, podcast 3-8; a very flat or wide mix gets a note), short_term_max_lufs (loudest 3 s), momentary_max_lufs (loudest 400 ms), true_peak_dbtp (4x oversampled) and sample_peak_dbfs, then pass and the problems against spec: online (-14 LUFS ±1, at most -1 dBTP: YouTube, Spotify, TikTok), ebu_r128 (-23 LUFS ±0.5, at most -1 dBTP: European broadcast) or atsc_a85 (-24 LKFS ±2, at most -2 dBTP: US TV); all three when spec is left out. loudness measures what export_video with that loudness delivers (the mix raised or lowered, its true peak held by the limiter), so a failing mix can be checked as exported: {spec: "ebu_r128", loudness: -23}. by_track measures each audio track alone as mixed (integrated, loudest three seconds, LRA) and, with a Voiceover track and music, the voice over the music in three named windows that differ by design: program (the whole stretch, music between lines included), speech (while the voice speaks, its lines together) and per_line (each line alone: lowest, median, highest, and where the lowest is). start and end (timeline seconds) measure part of it. It takes about a quarter of the mix's length to run (a 60 s mix, some 15 s), twice that with loudness.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
by_track |
boolean | Each audio track measured alone as mixed (before master normalization), with its role (describe_project names each track's; update_track sets one), and the voice's margin over the music (the dialog and music tracks, by role) by window: program, speech and per_line | |
per_second |
boolean | With by_track, each track's three-second loudness every second too (long; default false: summaries only) | |
kind |
"film" | "ad" | "social" | "podcast" | "explainer" | "any" | What the mix is for, to judge its loudness range: film (documentary, brand film), ad, social, podcast, explainer (one voice leading over a ducked bed: a tutorial, a narrated demo or product explainer) or any (default) | |
spec |
"online" | "ebu_r128" | "atsc_a85" | Delivery spec to check; default all three | |
loudness |
number | Measure as export_video with this loudness (LUFS) delivers it; default as mixed (-40..-5) | |
start |
number | Timeline seconds, default 0 (0..) | |
end |
number | Timeline seconds, default the end (0..) |
Example:
{"name":"measure_loudness","arguments":{"spec":"online","loudness":-14}}
export_video
Export the open project to a video file in the user's Videos folder (in a folder named after the app), or in folder, with no dialog; an existing file is never replaced: the new one takes a numbered name (Trip (2).mp4) and the answer's note says so, unless replace is true with folder: then a file of the same name in that folder is replaced (to supersede your own earlier export) and the note says so. Returns the file's path when done. loudness brings the mix to an integrated LUFS with its true peak held under -1 dBTP: -14 for YouTube, TikTok and Spotify, -23 for EBU R128 broadcast, -24 for ATSC A/85 US TV (held under -2 dBTP); the answer's loudness says what the mix reached. Each exported file answers the loudness its mix reached (loudness, info), as export_video does, and titles: a title behind its subject that the subject hides in that shape. Exports that take over about four minutes answer state running: follow them with export_status. format mp3, m4a or wav exports the sound alone. preview_media with file: the answer's path shows the exported file's own frames. captions decides what becomes of the captions (add_captions): burn (default) draws them into the picture, srt writes them as a .srt file beside the video (answered as captions_file) and leaves the picture clean, both does both, none leaves them out.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
name |
string | File name without extension; default the project's name | |
format |
"mp4" | "mov" | "webm" | "mkv" | "gif" | "mp3" | "m4a" | "wav" | Default mp4 (H.264 + AAC); mp3, m4a and wav are sound only | |
resolution |
"480p" | "720p" | "1080p" | "2k" | "4k" | Short side; default from the project | |
quality |
"lower" | "recommended" | "higher" | Default recommended | |
loudness |
number | Integrated LUFS for the mix; default as mixed (-40..-5) | |
start |
number | Export from this second (0..) | |
end |
number | Export to this second (0..) | |
captions |
"burn" | "srt" | "both" | "none" | The captions: burn into the picture (default), srt file beside it, both, or none | |
folder |
string | An absolute folder to write into instead of the Videos folder, created if missing (desktop only; the browser build refuses it rather than ignore it) | |
replace |
boolean | true: replace a file of the same name in folder (folder is required) when the export finishes, instead of taking a numbered name; nothing outside folder is touched |
Example:
{"name":"export_video","arguments":{"name":"spring-sale-16x9","resolution":"1080p","quality":"higher","loudness":-14,"folder":"D:/Deliveries/Spring sale"}}
export_versions
Export the deliverables of an ad in one call, each a separate file in the user's Videos folder (in a folder named after the app), or in folder (on the web, a save dialog per file), never replacing a file (a numbered name instead, with a note) unless replace is true with folder, the project left exactly as it is. A version is a ratio (16:9, 9:16, 1:1, 4:5, or original), optionally a duration in seconds (a cut-down: the opening and the end card are kept, the middle cut out, the music fading at the new end) and textless (titles and captions off: the master that other languages are cut from). Footage that fills the frame keeps filling it (fit fill), or fits over a blurred copy of itself (blur) or over the background (fit), and so does a shot fitted with bands (one spanning the frame edge to edge one way and covering its middle: a widescreen recording in a 9:16 master, pushed in with frame_region fit), which keeps its pixels against the short side and grows where that would not cover the new frame; where fill crops it (a 16:9 shot on 9:16 keeps a third of its width), the crop keeps what the editor chose: what the callouts over the shot point at (rings, a spotlight's hole, add_callout or anything pinned with an anchor), else the region the master frames (frame_region, a zoom past 1.1x: centered on it), else the shot's subject (what stands out in its pictures, looked at every half second), following it as it moves, inside what the master showed (a version wider than the master, 16:9 from 9:16, holds all of it, centered on it), keyed pushes and pans kept (pushes relax or hold eases them; reframe center keeps the old centered crop; focus [{clip_id, x, y}] puts a point of a clip's picture at the frame's center instead, and several with at make a path: each point centered at its time, held, panned to the next over 0.4 s); rings and spotlights over footage (and anything pinned with an anchor) stay on what they point at, moved and sized with the picture under them, a spotlight's hole with them, and a ring around a title stays around it; titles, logos and badges keep their size against the frame's short side and are moved inside its title-safe area (on 9:16 clear of the apps' buttons and captions); a long title wraps to the new frame's safe width, or shrinks to it; an effect's pixel sizes (a blur's radius, a pixelate's blocks) and a position wiggle keep their size against the picture they are on. A compound clip that draws only part of the frame (a title card built as a compound: a box, a headline, a subhead) refits as one picture, its parts keeping their layout; when its smallest type would fall under the readable size the whole card grows to meet it, and a card that then does not fit is restacked, its rows in order with their spacing, type held at the readable size, a panel behind them keeping its margins, nothing overlapping. A shape that spans the frame (a gradient scrim, a band) keeps its share of the new frame, as footage does. On a tall version (9:16, 4:5) of a wide film, what stood side by side is stacked in the safe column: a picture (a floating window, a split screen's half, cut again wider) above the title or picture beside it, each as wide as the column, what sat on one (a label, a glow) moving with it; a split screen whose halves filled the frame edge to edge fills the new frame instead (stacked full width on a tall version, side by side full height on a square), its labels inside the safe area. Text never ends under the frame's readable size, and on 4:5 overlays keep clear of the apps' buttons as on 9:16; every title is kept inside the area check_project judges it by, measured as it measures it, with room for the outline it may suggest. overrides [{clip_id, x, y, scale, hidden}] place a clip by hand in a version after all that (a lower third raised, a headline larger, a logo hidden), kept in a saved version project; keyed motion moves with it, and the rings, spotlights and pinned clips pointing into a picture moved so move with it. Every answer's options says what became of focus, pushes and overrides where that was not plainly what was asked (a focus on a clip the version does not crop, and why; a point held back by the picture's edge; pushes that found no keyed zoom; what moved with an override), and coverage names footage covering much less of a version's frame than of the master's (a picture floating in black), with what enlarges it. captions: burn (the default on tall versions, even from a hidden caption track: they sit in the column above the apps' captions, sized by the short side), none, or as_project (the default on the rest). A tall short's captions on a 16:9 or 1:1 version move to that frame's caption place (centered near the bottom, all at one height), and on 16:9 come down to a landscape caption's size (the typical caption at most size 72, 6.7% of the height; one made larger, a countdown, stays as much larger), never under the readable size; overlays keep above them, and a picture-in-picture that would cross them where the master kept it clear is moved above them (caption_overlaps says so in done), unless overrides place it. Ask with versions, or with ratios and durations (every ratio at every duration) plus textless for one textless master. check: true answers one contact sheet of every version (three moments each) and exports nothing: look at it first; an orange outline marks a subject a version's framing still cuts, and subjects lists each (with the focus that keeps it), and one the master's own push-in crops in a wider version (with pushes relax or hold, which show it); a title behind its subject (text_behind_subject) that the subject hides in a shape is outlined too and listed in titles, with the master's share to compare; a picture or graphic drawn across the burned captions in a shape (a picture-in-picture under them, measured by its mask) is outlined and listed in caption_overlaps, with the override that puts it above them; a ring, spotlight or pinned clip that runs off the frame or sits over other content than in the master is outlined and listed in callouts, with what keeps it (each exported version and saved project lists them too). save_projects: true saves each version as a project of its own instead (named as its file, sharing the media): open_project one to fine-tune it, then export_video. Exports that take over about four minutes answer state running: follow them with export_status. Example: {ratios: ['16:9', '9:16', '1:1'], durations: [15, 6], textless: true, loudness: -14}.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
versions |
{ ratio, duration, textless, fit, captions, name, reframe, focus, pushes, overrides }[] | Versions spelled out, e.g. [{ratio: '9:16', duration: 6}] | |
versions[].ratio |
"16:9" | "9:16" | "1:1" | "4:5" | "original" | yes | Frame shape; original keeps the project's |
versions[].duration |
number | Seconds; shorter than the project makes a cut-down (1..) | |
versions[].textless |
boolean | Titles and captions off | |
versions[].fit |
"fill" | "blur" | "fit" | How full-frame footage meets a new shape: fill (default), blur, fit | |
versions[].captions |
"burn" | "none" | "as_project" | burn the captions in (default on 9:16 and 4:5), none, or as_project (default on the rest) | |
versions[].name |
string | File name without extension; default the project's name and the version, e.g. 'Roast 9x16 6s' | |
versions[].reframe |
"subject" | "center" | Footage the new shape crops: framed on its subject, following it (default), or center | |
versions[].focus |
{ clip_id, x, y, at }[] | Clips framed by hand: [{clip_id, x, y}], the point of the clip's picture (fractions from its top left, as preview_media shows it) to keep at the center of the frame; an axis left out stays on what the version frames by itself (the callouts over it, else the region the master frames, else the subject). Several for one clip, each with at, make a path: each point centered at its time, held until the next, panning to it over the 0.4 s before (a ring gliding through several parts stays in frame; callouts already lead the crop this way without a focus). A focus that cannot apply (a picture-in-picture, a title, a clip the shape does not crop) is named in the answer's options with why. On the call, for every version that does not say | |
versions[].focus[].clip_id |
string | yes | A video or picture clip that fills the master's frame, or spans it edge to edge with bands |
versions[].focus[].x |
number | Across its picture, 0 left to 1 right (0..1) | |
versions[].focus[].y |
number | Down its picture, 0 top to 1 bottom (0..1) | |
versions[].focus[].at |
number | Timeline seconds of the master this point is centered at, for a path of several (0..) | |
versions[].pushes |
"keep" | "relax" | "hold" | Keyed pushes of full-frame shots and of shots fitted with bands (a zoom keyed by hand or by frame_region): keep (default), relax (half the zoom) or hold (the shot at its widest throughout). A tall master's push-in frames a narrow column; on a wider version the same zoom crops the picture's top and bottom (a rocket's nose), and relax or hold shows them, the frame still around what the master showed; when no shot has a keyed zoom the answer's options says so. On the call, for every version that does not say | |
versions[].overrides |
{ clip_id, x, y, scale, hidden }[] | Clips placed by hand in this version, after the automatic adaptation (raise a lower third, enlarge a headline, hide a logo): [{clip_id, x, y, scale, hidden}]. Any clip: a title, a picture, a compound clip, or a clip inside a compound by its own id. A clip in a group (an ad element) moves and resizes with its group; hidden is the clip alone. Kept in a saved version project. On the call, for every version that does not say | |
versions[].overrides[].clip_id |
string | yes | The clip, as describe_project names it in the master |
versions[].overrides[].x |
number | Where it rests across the version's frame: its transform.x, a fraction of the frame from its center (-0.5 left edge .. 0.5 right edge); keyed motion moves with it, and so do the rings and pinned clips pointing into a picture (-1..1) | |
versions[].overrides[].y |
number | Where it rests down the frame: its transform.y (-0.5 top .. 0.5 bottom; on a tall frame a title's box stays above y 0.15, where the apps' captions and buttons begin) (-1..1) | |
versions[].overrides[].scale |
number | Times its size as the version adapted it (1.25 a quarter larger: a title's type, any other layer's scale), about its own center (0.1..10) | |
versions[].overrides[].hidden |
boolean | true: off in this version; false: on, though the master hides it | |
ratios |
"16:9" | "9:16" | "1:1" | "4:5" | "original"[] | Every one of these shapes... | |
durations |
number[] | ...at every one of these lengths in seconds (one as long as the project is the full version); e.g. [30, 15, 6] | |
textless |
boolean | Also a textless version, in the first ratio | |
fit |
"fill" | "blur" | "fit" | For every version that does not say: fill (default), blur or fit | |
captions |
"burn" | "none" | "as_project" | For every version that does not say: burn, none or as_project; default burn on tall versions, as_project on the rest | |
reframe |
"subject" | "center" | For every version that does not say: subject (default) or center | |
focus |
{ clip_id, x, y, at }[] | Clips framed by hand: [{clip_id, x, y}], the point of the clip's picture (fractions from its top left, as preview_media shows it) to keep at the center of the frame; an axis left out stays on what the version frames by itself (the callouts over it, else the region the master frames, else the subject). Several for one clip, each with at, make a path: each point centered at its time, held until the next, panning to it over the 0.4 s before (a ring gliding through several parts stays in frame; callouts already lead the crop this way without a focus). A focus that cannot apply (a picture-in-picture, a title, a clip the shape does not crop) is named in the answer's options with why. On the call, for every version that does not say | |
focus[].clip_id |
string | yes | A video or picture clip that fills the master's frame, or spans it edge to edge with bands |
focus[].x |
number | Across its picture, 0 left to 1 right (0..1) | |
focus[].y |
number | Down its picture, 0 top to 1 bottom (0..1) | |
focus[].at |
number | Timeline seconds of the master this point is centered at, for a path of several (0..) | |
pushes |
"keep" | "relax" | "hold" | Keyed pushes of full-frame shots and of shots fitted with bands (a zoom keyed by hand or by frame_region): keep (default), relax (half the zoom) or hold (the shot at its widest throughout). A tall master's push-in frames a narrow column; on a wider version the same zoom crops the picture's top and bottom (a rocket's nose), and relax or hold shows them, the frame still around what the master showed; when no shot has a keyed zoom the answer's options says so. On the call, for every version that does not say | |
overrides |
{ clip_id, x, y, scale, hidden }[] | Clips placed by hand in this version, after the automatic adaptation (raise a lower third, enlarge a headline, hide a logo): [{clip_id, x, y, scale, hidden}]. Any clip: a title, a picture, a compound clip, or a clip inside a compound by its own id. A clip in a group (an ad element) moves and resizes with its group; hidden is the clip alone. Kept in a saved version project. On the call, for every version that does not say | |
overrides[].clip_id |
string | yes | The clip, as describe_project names it in the master |
overrides[].x |
number | Where it rests across the version's frame: its transform.x, a fraction of the frame from its center (-0.5 left edge .. 0.5 right edge); keyed motion moves with it, and so do the rings and pinned clips pointing into a picture (-1..1) | |
overrides[].y |
number | Where it rests down the frame: its transform.y (-0.5 top .. 0.5 bottom; on a tall frame a title's box stays above y 0.15, where the apps' captions and buttons begin) (-1..1) | |
overrides[].scale |
number | Times its size as the version adapted it (1.25 a quarter larger: a title's type, any other layer's scale), about its own center (0.1..10) | |
overrides[].hidden |
boolean | true: off in this version; false: on, though the master hides it | |
save_projects |
boolean | true: save each version as a project of its own to fine-tune (open_project it, edit, export_video), and export nothing | |
keep_end |
number | Seconds of the end every cut-down keeps; default the end card's length (add_ad_element end_card), else 0 (0..) | |
check |
boolean | true: answer a contact sheet of every version and export nothing | |
check_at |
number[] | The moments the check sheet shows, as fractions of each version's length, up to 8; default [0.15, 0.5, 0.92]. A version's moments wrap into rows at 1600 px | |
tile_width |
integer | Width of each frame on the check sheet, its height following the version's shape; default frames 220 px tall (a 16:9 frame 391 px wide) (120..800) | |
format |
"mp4" | "mov" | "webm" | "mkv" | Default mp4 (H.264 + AAC) | |
resolution |
"480p" | "720p" | "1080p" | "2k" | "4k" | Short side; default from the project | |
quality |
"lower" | "recommended" | "higher" | Default recommended | |
loudness |
number | Integrated LUFS for every version's mix, e.g. -14; default as mixed (-40..-5) | |
folder |
string | An absolute folder to write into instead of the Videos folder, created if missing (desktop only; the browser build refuses it rather than ignore it) | |
replace |
boolean | true: replace a file of the same name in folder (folder is required) when the export finishes, instead of taking a numbered name; nothing outside folder is touched |
Example:
{"name":"export_versions","arguments":{"ratios":["16:9","9:16","1:1"],"durations":[15,6],"textless":true,"loudness":-14,"check":true}}
export_status
The last export_video: running (fraction done, eta seconds), done (path, bytes, seconds) or failed (message).
No parameters.