Drive ExpoCut with your AI agent.
A Model Context Protocol reference for ExpoCut's editing surface — 263 typed tools an AI assistant can call to plan, draft, and build videos with you, right on your phone.
A vocabulary, not an API.
ExpoCut is a mobile-first video editor. This page documents the conceptual tools, resources, and prompts an AI assistant can reference when guiding a creator. It mirrors the shape of the Model Context Protocol so agents can index it and reason about what ExpoCut can do.
Mobile-native
Hardware-accelerated rendering on iOS and Android with native, device-level video encoders.
Multi-track timeline
Unlimited video, audio, image, and text layers with frame-accurate trim, split, and ripple edit.
4K export
Up to 4K UHD at 60fps with H.264 / HEVC and configurable bitrate.
Three views of the same idea.
An AI assistant on your laptop talks to ExpoCut on your phone. The connector is a thin wrapper; the editor does the work. Here is the whole picture.
Connect from any MCP-compatible client. The connector lives inside the App, accepts requests only from private or loopback network addresses, and exposes a typed tool surface the agent can call. Seven popular clients shown — bring your own.
A natural-language request flows through the agent, into the connector, through the editor's command bus, and back as an undoable, auditable result.
Every tool the agent can call maps to a real user action — composed by the same scene graph that powers the touch UI. Reach any family in one hop from the engine.
From zero to a rendered clip.
Turn the connector on, point your AI client at it, and let the assistant call the tools. Here is the shortest path to a finished video.
Enable the connector
In ExpoCut → Settings, switch on the in-app MCP server. It is off by default and accepts requests only from private or loopback network addresses on the device.
Connect your AI client
Point any MCP-compatible client at the local address ExpoCut shows. For Claude CLI:
claude mcp add --transport http expocut \ http://<device-ip>:<port>/mcp \ --header "Authorization: Bearer <token>"
Address and token appear in Settings once the connector is on.
Ask, then let it build
Describe the video. The assistant plans a sequence of tool calls — like the one below — and you watch it take shape on the canvas, every step undoable.
// 1 · start a project and open it in the editor create_project { name: "Beach Reel", aspectRatio: "9:16", fps: 30 } open_project { id } // 2 · add a stock clip, then a titled intro over it add_stock_video_layer { query: "ocean sunrise", duration: 5 } add_text_layer { text: "SUMMER", fontSize: 96, verticalAnchor: "center" } set_text_animation { layerId, inId: "in-fade", inDurationSec: 0.6 } // 3 · look at a frame, then render describe_canvas { timeSec: 1.5 } // cheap, image-free layout readout capture_canvas { timeSec: 1.5 } // a real frame the model can see export_project {} // → file://…mp4
iOS: how to connect
ExpoCut and your AI client must be on the same Wi‑Fi network. iPhone and iPad also require Local Network access.
- In ExpoCut, open Settings → AI Agent (MCP Server) and turn on Enable.
- When iOS asks, choose Allow for Local Network. If the prompt was dismissed, enable ExpoCut under Settings → Privacy & Security → Local Network.
- Copy the displayed Server URL and Bearer Token into your MCP client, then send it a test request.
Eight rules every tool follows.
Assume these conventions — they hold across the whole surface, so each tool's docs don't repeat them.
| Time is seconds | All startTime / duration / *Sec arguments are in seconds. |
| Position is top-left % | x / y are 0–100 % of the canvas; full-canvas layers pass stretchToCanvas: true. |
| Open a project first | Layer & setter tools need an open project — call create_project then open_project (it auto-navigates to the editor). |
| Editor must be mounted | capture_canvas, preview_filmstrip and export_project drive the live editor; open_project makes this just work. |
| Catalog before setter | Tools that take an id reject unknown ids with a hint pointing back to the matching list_* / get_effect_schema. |
| Z-order: 0 is front | New layers land at trackIndex 0 (front); everything else shifts back. Use reorder_layer to restack. |
| Discover at runtime | Catalogs evolve — call list_effects, list_shaders, list_transitions, list_fonts, list_shapes… for the live set. |
| Every call is undoable | Each tool call is recorded as a reversible step in the on-device edit history. |
What an assistant can suggest.
Each tool maps to a real action a user performs in the ExpoCut UI. An AI assistant can compose them into a full editing plan.
Start a new project
Create a new edit at a target resolution and aspect ratio. Templates available for Reels, TikTok, YouTube, and Shorts.
Bring in clips
Import video, audio, or image media from the device gallery, Pexels stock, Freesound audio, or a remote URL.
Place a layer on the timeline
Add a clip, photo, sticker, text, lower third, or animation overlay onto a track at a specific in-point.
Trim & split
Adjust in/out points or split a clip at the playhead. Ripple edits keep the rest of the timeline aligned.
Speed — slow-mo & fast-forward
Retime a video or audio clip. 2× plays twice as fast and shortens the clip; 0.5× is slow motion. Keep-pitch holds the audio natural, or let it varispeed.
Apply visual effects
Choose from 220+ effects and 220+ preset filters & looks — 7 parametric engines including Cinematic Grade (skin-tone protection, film halation, split-tone wheels), Warp & Distort, Retro Display, Edge & Sketch, Light FX, Cartoon and Light Flicker, plus blur, glitch, chroma key, vignette and cinematic LUTs. See them live →
Add transitions
Place one of 99 transitions between adjacent clips: fade, dip-to-black, slide, push, zoom, glitch, whip pan, shader wipes. See them live →
Titles & captions
Add styled text or one of 180 animated lower thirds with custom font, color, position, and entrance.
Mix & sweeten audio
Adjust volume, ducking, fade in/out, and apply 47 audio effects across 35 audio transitions.
Color grade
Exposure, contrast, saturation, temperature, tint, shadows, highlights — applied per-clip or globally.
Render & export
Encode the final timeline using the on-device hardware encoder. Saves to camera roll or shares via system sheet.
Capture a frame · with debug views
Render the current canvas at a given playhead position and return a self-describing screenshot — project size, total length, and the layers actually visible at that frame (with their computed bounding boxes in canvas %). Toggle xray to dim the composition under labeled layer boxes, outlinesOnly for a borders-only view, and grid for a 10%-step coordinate overlay with % labels — pin-pointing layout bugs in one tool call.
Read pixels at a point
Sample the rendered canvas — useful for AI vision tools that need to check what's actually on-screen.
Validate a TTS script
Run a script through the TTS pre-flight: catches unsupported characters, unpronounceable tokens, and estimated duration.
Discover effect parameters
Look up the typed parameter schema for any effect, filter, or transition so an assistant can compose valid calls.
Remove background
On-device segmentation. Produces an alpha mask on a clip or image without uploading frames.
Generate captions
Transcribe spoken audio on-device and place time-aligned caption layers on the timeline, styled with a chosen preset. See transcript styles live →
Transcribe audio to text
Run on-device speech-to-text against an audio layer and get back time-aligned segments — input to captions, chapters, or summaries.
Reorder / hide layers
Change z-order, bring-to-front, send-to-back, or temporarily hide a layer for A/B compare.
Add a dynamic widget
Place a countdown timer or stopwatch as a first-class timeline layer with editable target time and styling.
Import a third-party project
Ingest a motion-graphics template, an editing-project XML, an animated-graphic JSON, or a color file — parsed entirely on-device into a native ExpoCut project.
Add a shape
Browse the full library of 91 shape presets — basic geometry, arrows, stars & bursts, callouts & speech bubbles, badges, ribbons, lines and rulers — and drop any one on the timeline with fill, gradient, or stretch-to-canvas.
Add a brush stroke
Browse 134 hand-drawn brush strokes across 14 categories — arrows, encircles & frames, underlines & highlights, sparkles, neon one-liners, comic speedlines, lightning, splats, ink, marker, paint, dry brush, spray and scribbles — and drop any one on the timeline. Brushes are vector shapes, so fills, gradients, outlines and glow all apply.
Fade on edge
Apply a spatial gradient alpha fade to any image, video, or shape — a directional linear gradient, a radial vignette, or a four-edge inset feather. Distinct from the time-based fade-in / fade-out ramps.
Light leak overlay
Browse the light-leak catalog — warm sunset, cool window, prism, neon, sun flare and more — and lay a real-footage leak over the current content on its own track, with adjustable blend intensity. See light leaks live →
Assemble, tighten, clean, and light a cut.
These editing primitives are callable directly from an agent: build a multi-scene sequence, remove dead air, reduce a long video to its strongest passages, clean dialogue, and apply localized shader or lighting treatments without leaving the project.
Build a sequence
Compose a multi-scene edit from local, stock, or remote clips with per-clip duration, transitions, and optional audio.
Auto-edit the pacing
Detect and ripple-delete silent gaps, or select the most energetic passages up to a target duration while preserving the source layer.
Clean dialogue
Run on-device denoise and speech enhancement on a video or audio layer, with the result available to the same export pipeline.
Layer shader filters
Apply the 7 parametric filter engines by named Look preset (e.g. "Teal & Orange") or raw params, stack up to 4 different engines per layer with append, and remove one or all — same render path as the canvas and export.
Localized lighting
Add, inspect, or clear area, gradient, radial, object, and trident lighting regions for a layer without changing the base grade.
Brushes, junction transitions & frame-accurate render tools.
The newest additions: a 134-stroke brush library (list_brushes · add_brush_layer), cut-level junction transitions (set_junction_transition), a virtual camera (set_camera), and off-screen render tools for export-parity checks — on top of the importer, codec, color & audio batch below. Each is callable from any MCP-compatible client.
Brush strokes
134 hand-drawn vector strokes in 14 categories — arrows, encircles, underlines, sparkles, neon one-liners, comic speedlines, lightning — added as layers with the full fill / gradient / outline / glow system.
Frame-accurate verification
Render a fully-composited frame off-screen, grab the exact encoder frame, and diff it against the editor canvas — agents catch preview↔export drift before delivery.
Importer
Ingest a motion-graphics template, an editing-project XML, an animated-graphic JSON, or a color file — from an inline payload or from the local filesystem. Sniff the format, migrate the data model, and route to the right parser.
Codec compatibility
Ask whether a target codec, profile, and container will play on a given device class — and degrade-and-warn when it won't, so the agent's export plan never produces a file the device can't open.
Loudness pipeline
Measure integrated loudness, compute the gain needed to hit a broadcast or streaming target (EBU R128, ATSC A/85, –14 LUFS), and produce a normalization plan ready to apply to the timeline.
Color science
Parse a .cube LUT, write an ASC CDL/CCC color-decision file, and bake a CDL into a 3D LUT — round-trip color from grading session to final render.
Font fallback
Given a font name and a glyph set, resolve to the best available family on the current device and report coverage gaps — keeps imported templates legible everywhere.
Cross-format export
Round-trip an ExpoCut template out to Lottie JSON (with animated keyframes) or to FCPXML (with embedded CDL) — let an agent hand a finished project back to the user's NLE.
Keyframes — animate any property on image, text, shape & video.
The keyframe substrate now accepts image, text, shape, and video layers for the shared transform / opacity / fx / border / mask / color surface. Nine new MCP commands expose every authoring path the in-app keyframe editor uses — scalar tracks, held-step discrete tracks, snapshot columns, easing curves, and bulk plans.
Add a scalar keyframe
Place a value on any animatable scalar property at a given time. The optional interp chooses the easing curve leaving the keyframe.
Add a held-step keyframe
For properties that don't interpolate — font weight, italic, mask shape, transition id, fill style. Value type follows the property (string / number / boolean).
Delete a column
Remove every keyframe at a given time across all tracks — mirrors the editor's "delete column" UX. Times match with ~1 ms tolerance.
Reset animation
Clear all keyframes on a layer, or just one property. The "Reset" button as a tool call.
Inspect a layer's animation
Return the full LayerAnimation JSON — every scalar track, position track, and discrete track. Use before authoring to avoid double-keyframing.
Refine an easing curve
Replace the easing leaving an existing keyframe without touching the value. Use to retune motion without re-adding keyframes.
Capture current state
Take a snapshot of the layer's current transform + opacity + colour values and write a column of keyframes at the given time — useful as the "before" state before authoring a change.
Bulk-replace the whole animation
Submit a full LayerAnimation JSON to replace every track on a layer in one call. Validates each track's property against the target layer type — returns PropertyMismatch on a bad pairing.
Catalog animatable properties
Catalog every animatable property — scalar and discrete — with the layer types each applies to. Optional layerType filter narrows to image / text / shape / video / audio.
Which properties apply where
Call list_keyframe_properties { layerType } for the exact list. Authoring an invalid pair returns PropertyMismatch.
| Property family | image | text | shape | video | audio |
|---|---|---|---|---|---|
| transform.* · opacity | ✓ | ✓ | ✓ | ✓ | — |
| color.* · fx.* · filter.* | ✓ | ✓ | ✓ | ✓ | — |
| border.* · mask.* | ✓ | ✓ | ✓ | ✓ | — |
| transition.* | ✓ | ✓ | ✓ | ✓ | — |
| text.* (color/stroke/font/align) | — | ✓ | — | — | — |
| shape.fillStyle | — | — | ✓ | — | — |
| audio.volume | — | — | — | ✓ | ✓ |
Audio layers keyframe audio.volume directly — but most audio shaping is generated, not hand-keyed. The on-device stem-separation suite below writes ducking envelopes onto the music layer, drives any layer property from a drum/bass envelope, and returns beat-cut times — no manual keyframes required.
Easing curves
Every interp argument accepts one of four shapes. Times are integer microseconds internally — pass milliseconds via the MCP boundary.
// hold — value snaps; useful for discrete-like behaviour on a scalar track { "type": "hold" } // linear — straight-line interpolation { "type": "linear" } // CSS-compatible cubic-bezier (P1.x & P2.x clamped to [0,1]; P1.y / P2.y may overshoot) { "type": "bezier", "x1": 0.42, "y1": 0, "x2": 0.58, "y2": 1 } // Named preset (allow-listed) { "type": "preset", "name": "easeInOut" } // presets: "ease" | "easeIn" | "easeOut" | "easeInOut" | "bounce" | "elastic" | "spring"
Example — fade in + scale-up on a text layer
Pop the layer in over 600 ms with spring easing on scale and a linear opacity ramp.
keyframe_add { layerId: "text_abc", property: "opacity", timeMs: 0, value: 0 } keyframe_add { layerId: "text_abc", property: "opacity", timeMs: 600, value: 1 } keyframe_add { layerId: "text_abc", property: "transform.scale", timeMs: 0, value: 0.4, interp: { type: "preset", name: "spring" } } keyframe_add { layerId: "text_abc", property: "transform.scale", timeMs: 600, value: 1 }
Or use keyframe_set_animation with the full LayerAnimation JSON to author the same plan in one call. Call list_keyframe_properties { layerType: "text" } first to see every property a text layer accepts.
Save locally, or share a reusable edit.
These are two different operations. Keep a working copy in My Templates for reuse on the device; submit a full-fidelity snapshot only when the author explicitly wants a community share.
Save to My Templates
mode:"template" (default) turns text/media into editable slots and keeps the reusable recipe. mode:"snapshot" preserves masks, invert/mirror geometry, effects, morphs, keyframes, looks, styles, media references, and export settings exactly as authored. This writes to the on-device template library and does not publish anything.
Share to Community
Snapshots the open project, validates it, packages safe local assets into an .ectpl, and shares it with the ExpoCut Community. An optional thumbnail and rendered demo MP4 can be included. Private video/image/audio URIs are stripped by default; masks, effects, styles, looks, and keyframes remain.
Retention is once, 1d, 7d, 21d, or 30d. Save the returned ownerKey; then use list_my_submissions and delete_my_submission to manage only uploads from this device.
Recommended agent flow: save_project_as_template(mode:"snapshot") → render/capture a demo and thumbnail → ask for explicit permission to share source media → submit_template → return the share URL and expiry.
On-device audio intelligence — split a song into stems.
Most editors keyframe one thing on audio: volume. ExpoCut splits any clip into vocals · drums · bass · other stems on the device — nothing uploaded — then composes that into karaoke tracks, dialogue boosts, sidechain ducking, beat-synced cuts, and audio-reactive motion. Eight tools, all non-destructive (the source clip is never touched; stems are added as their own layers).
Split into stems
Separate a clip into vocals / drums / bass / other as four new audio layers. tier:"fast" is a quick, model-free split; tier:"studio" is a high-quality neural split (falls back to fast if it can't run). Runs entirely on-device.
Karaoke / instrumental
One call: split and mute the vocal stem, leaving a clean instrumental bed. Non-destructive.
Dialogue boost
Raise the vocal stem and duck the rest — clean up talking-head and interview clips where the voice sits under the music.
Per-stem faders
Set volume (0–1) and/or mute on already-separated stems — push the drums, pull the vocal back. Faders write to the stem layers, so it stays non-destructive.
Beat-synced cut times
Detect onsets on the isolated drum stem and return cut times in ms — feed them straight into split_layer or set_layer_transition for an edit that lands on the beat.
Sound-reactive motion
Bind a stem's loudness envelope to a layer property — zoom-pulse on the kick, flash on the snare, wobble on the bass. The motion follows the music automatically.
Sidechain ducking
Detect where the voice is active and write volume keyframes that dip the music underneath it — broadcast-style ducking without drawing a single envelope.
Copyright-safe strip
Mute the instrumental stems so you can re-layer a licensed track — keep the original vocal/performance, replace the bed.
Example — beat-synced, voice-forward edit
Split the track once, then duck the music under the voiceover and pull cut times off the drums — three calls.
separate_audio { layerId: "music_0", tier: "studio" } auto_duck { musicLayerId: "music_0", vocalSourceLayerId: "vo_0", duckDb: -12 } beat_cut_from_drums { sourceLayerId: "music_0", maxCuts: 8 } // → cutTimesMs: [508, 1016, …]
All eight live under the Audio separation · stems family. Separation, detection and mixing all run on-device — audio never leaves the phone.
All 263 tools, by family.
Every command the connector registers. Names are the canonical tool IDs — copy-paste into your agent's tool catalog. Green chips are recently shipped.
Projects
8 TOOLSLayers · basic
8 TOOLSMedia layers
5 TOOLSStock & music
9 TOOLSCuts & order
5 TOOLSEffects & polish
23 TOOLSMasks & procedural
13 TOOLSTransitions
6 TOOLSTwo kinds of transition. Layer tools set a clip's own entrance / exit. Junction tools set the blend on the cut between two adjacent clips — a different thing, addressed by the pair of layers that meet there rather than by one layer.
Color
1 TOOLText & animation
8 TOOLSTTS & Speech-to-Text
5 TOOLSAudio separation · stems
8 TOOLSSequences & auto-edit · NEW
4 TOOLSCatalogs
20 TOOLSCanvas, preview & render
14 TOOLSImporter · NEW
5 TOOLSCodec · NEW
3 TOOLSLoudness · NEW
4 TOOLSColor writers · NEW
2 TOOLSCross-format export · NEW
2 TOOLSFont fallback · NEW
3 TOOLSFX Authoring · NEW
5 TOOLSLUT Authoring · NEW
3 TOOLSBorder Authoring · NEW
3 TOOLSMask Authoring · NEW
3 TOOLSKeyframes · NEW
9 TOOLSMotion path & text-2 · NEW
19 TOOLSAudio companion & layout · NEW
11 TOOLSAdvanced compositing · NEW
12 TOOLSWidget config · NEW
3 TOOLSTemplates & Reels · NEW
11 TOOLSBrand profiles · NEW
6 TOOLSSession control · NEW
16 TOOLSIntrospection & verify · NEW
5 TOOLSBuiltins
2 TOOLS
Param schemas live in llms-full.txt. Call get_effect_schema at runtime for the canonical signature of any effect.
After Effects-class motion. CapCut-class speed.
ExpoCut's tool surface composes into the same moves agents already know from desktop NLEs and social editors. Below: ten copy-paste plans an AI assistant can drop on the timeline to produce highly creative output — images and shapes animate as first-class citizens via the keyframe substrate.
Logo / image scale-in with bounce
Animate any image or shape from 0 → 1.08 → 1.0 with an overshoot — the AE classic in three keyframes. interp presets do the easing math.
keyframe_add { layerId, property: "transform.scale", timeMs: 0, value: 0.0, interp: { type: "preset", name: "easeOut" } } keyframe_add { layerId, property: "transform.scale", timeMs: 420, value: 1.08, interp: { type: "preset", name: "easeIn" } } keyframe_add { layerId, property: "transform.scale", timeMs: 560, value: 1.0 }
Shape slides across with motion blur fake
Move a shape left → right while ramping fx.blur for a streak feel — both tracks are scalar keyframe properties on the same layer.
keyframe_add { layerId, property: "transform.x", timeMs: 0, value: -40 } keyframe_add { layerId, property: "transform.x", timeMs: 600, value: 50, interp: { type: "preset", name: "easeInOut" } } keyframe_add { layerId, property: "fx.blur", timeMs: 0, value: 0.6 } keyframe_add { layerId, property: "fx.blur", timeMs: 600, value: 0.0 }
Continuous rotation on a shape badge
Two keyframes, linear interp — drop in any seal / record-button / loading element. Replay keyframe_set_interp to ease.
keyframe_add { layerId, property: "transform.rotation", timeMs: 0, value: 0 } keyframe_add { layerId, property: "transform.rotation", timeMs: 2000, value: 360 }
Image revealed by an expanding rectangle
Animate the mask rect from 0% → 100% width — the classic AE box-reveal. Combine with mask.feather for a soft edge.
set_layer_mask { layerId, shape: "rectangle", rect: { x: 0, y: 0, width: 0, height: 1 }, feather: 4 } keyframe_add { layerId, property: "mask.rect.width", timeMs: 0, value: 0 } keyframe_add { layerId, property: "mask.rect.width", timeMs: 700, value: 1, interp: { type: "preset", name: "easeOut" } }
Cut on every real onset of the drum stem
No BPM guesswork — separate the track, run onset detection on the isolated DRUM stem, then split the visuals at the actual hits.
separate_audio { layerId: "music", tier: "fast" } beat_cut_from_drums { sourceLayerId: "music", sensitivity: 1.2, maxCuts: 6 } # → { cutTimesMs: [508, 1016, 1524, 2031, 2554, 3070] } split_layer { layerId: "v0", atSec: 0.508 } # atSec is relative to the layer's own startTime split_layer { layerId: "v1", atSec: 0.508 } set_layer_transition { layerId: "v1", out: { id: "slicewipe", durationSec: 0.18 } }
Put the blend on the cut, not on the clip
A junction is the seam where two clips touch. It is derived from clip adjacency, so it has no permanent id — list_junctions resolves the current cuts, and the setter addresses one by the pair of layers that meet there. This is also the only route to the reveal's origin and its rim refraction.
list_junctions {} # → { count: 1, junctions: [{ fromLayerId: "v0", toLayerId: "v1", atSec: 4, # maxDurationSec: { center: 4, start: 4, end: 4 }, transition: null }] } # A droplet reveal that opens from where the action is, not from dead centre. set_junction_transition { fromLayerId: "v0", toLayerId: "v1", effectId: "wipe.iris", durationSec: 0.6, params: { originX: 0.82, originY: 0.71, bulge: 0.8 } } # → resolvedOptions echoes the post-clamp values the shaders actually run, # and durationClamped tells you if the cut could not hold what you asked for. # Directional motion smear instead of an even blur. set_junction_transition { fromLayerId: "v1", toLayerId: "v2", effectId: "distort.warp", params: { blur: 0.6, blurAniso: 1, blurAngle: 45 } }
Params are merged onto whatever the cut already had, so adjusting one option never resets the rest of the look. clear_junction_transition puts the straight cut back.
On-device captions, time-aligned to the source audio
Whisper transcribes the spoken language and the segments land straight into a positioned transcript layer — one call, bottom-safe by default.
add_caption_layer_from_audio { uri: "voiceover.wav", language: "auto", startTime: 0, x: 50, y: 82 }
Snap-zoom into a clip on the drop
Hold scale at 1.0, then snap to 1.18 at the drop with a hard easeIn — the CapCut "punch" trick. Reset at the next cut.
keyframe_add { layerId, property: "transform.scale", timeMs: 0, value: 1.0 } keyframe_add { layerId, property: "transform.scale", timeMs: 980, value: 1.0, interp: { type: "preset", name: "easeIn" } } keyframe_add { layerId, property: "transform.scale", timeMs: 1020, value: 1.18 } keyframe_add { layerId, property: "transform.scale", timeMs: 1480, value: 1.0 }
Teal-orange cinematic grade with LUT bake
Bake an ASC-CDL (slope/offset/power/saturation) to a .cube LUT, register it as a custom LUT, then apply it to every video/image clip in one pass — the round-trip a colorist would use.
bake_cdl_to_cube { cdl: { slope:[1.05,1.02,0.96], offset:[0.01,0.00,-0.02], power:[1.0,1.0,1.05], saturation: 1.12 }, size: 33 } # → { cube: "TITLE \"...\" LUT_3D_SIZE 33 ...", size: 33 } lut_register_custom { id: "cinematic-teal-orange", name: "Cinematic Teal-Orange", cubeText: "<cube text from above>" } apply_global_color_grade { lutId: "cinematic-teal-orange", lutIntensity: 0.65 }
Compose a custom whip-pan from a curated template
No raw GLSL — pick a curated distortion family, override its typed params, and the spec is immediately usable as an effectId.
fx_compose_from_template { templateId: "transition.distortion.warp", name: "Whip Pan Soft", params: { strength: 1.4, angle: 90 }, durationMs: 450 } # → { ok: true, id: "ai.whip-pan-soft-..." } set_layer_effect { layerId, effectId: "ai.whip-pan-soft-..." }
Cut subject out, slot onto a generative backdrop
ML person segmentation removes the original background; an animated shader gradient sits underneath. Zero uploads, zero green screen.
set_layer_background_remover { layerId: "subject", quality: "balanced", featherPx: 3 } add_generative_bg_layer { effectId: "auroragradient", preset: "Soft Violet", startTime: 0, duration: 30 } reorder_layer { layerId: "subject", position: "front" }
Every recipe is a pure tool composition. Agents can mix them: e.g. recipe 1 (scale pop) + recipe 4 (mask wipe) + recipe 8 (teal-orange grade) is a complete title-card.
What an assistant can reference.
Catalogs are discovered at runtime through typed list_* tools — the live set, never a stale snapshot. (MCP resources/* and prompts/* endpoints are not part of the v1 server.)
- list_effects · get_effect_schema150+ visual effects, 7 parametric filter engines and 220+ preset filters & looks with names, categories, and previews.
- list_transitionsVideo transitions including cinematic, social, and glitch styles.
- list_lower_thirds180 animated lower thirds (incl. RTL sets) with editable text and brand color slots.
- list_audio_effects · list_audio_transitions47 audio effects and 35 audio transitions for music and voiceover.
- list_shapes · list_brushes91 shape presets and 134 brush strokes, addable as tintable design layers.
- stock_search_videos · stock_search_photosIn-app Pexels integration for royalty-free stock video and photos.
- stock_search_musicStock audio search for SFX and ambience.
- get_canvas_infoSupported social formats: Reels 9:16, YouTube 16:9, Square 1:1, Portrait 4:5.
See them move — live, in-browser previews in the Showcase:
Eight productions. Eight plans.
Each plan below is a current production pattern — intake, structure, tool composition, visual verification, and export — exactly as a senior assistant should respond. Copy one into a system prompt, then adapt catalog ids at runtime. These examples use the current 251-tool surface, including shape-aware mask graphs, curve interpolation, filmstrip preview, export-parity checks, and reusable template sharing; every call is a real MCP command.
"Build a 30-second travel reel with beat cuts, a soft mask reveal, and captions."
Use real drum-stem onsets for the cut points, a shape-aware mask animation for the opening shot, and a filmstrip review before export. The handoff includes a canvas-versus-MP4 parity check and an optional reusable template snapshot.
# Intake (assistant asks three short questions) Q1: Platform & length? → Reels, 30s, 9:16, 1080p, 30fps Q2: Vibe? → Upbeat travel, "I want it to feel alive" Q3: Music? → "Surprise me — something cinematic-pop" # Structure (3-part) Hook (0–2.5s): strongest beach drone push-in. Middle (2.5–26s): cuts on the REAL drum hits — temple, food, scooter, sunset, waterfall, friend. Payoff (26–30s): wide drone with location title + soft fade-to-grade. # Plan — 24 calls create_project { name: "Bali Reel", aspectRatio: "9:16", fps: 30 } add_video_layer × 8 # import 8 gallery clips → v0..v7 stock_search_music { query: "cinematic pop travel", category: "cinematic" } add_stock_music_layer { soundId: 4421, startTime: 0, duration: 30, volume: 0.85 } # Hook — drone push-in trim_layer { layerId:"v0", mediaOffsetSec: 0.4, durationSec: 2.5 } keyframe_add { layerId:"v0", property:"transform.scale", timeMs:0, value:1.0 } keyframe_add { layerId:"v0", property:"transform.scale", timeMs:2500, value:1.06, interp:{type:"preset",name:"easeOut"} } # Beat-detect the music's drum stem — real onsets, not BPM guesswork separate_audio { layerId:"music", tier:"fast" } beat_cut_from_drums { sourceLayerId:"music", sensitivity: 1.3, maxCuts: 6 } # → cutTimesMs: [2480, 6610, 10720, 14790, 18930, 22980] — split each clip at its own onset split_layer { layerId:"v1", atSec: 2.48 } # temple split_layer { layerId:"v2", atSec: 1.30 } # food split_layer { layerId:"v3", atSec: 1.40 } # scooter split_layer { layerId:"v4", atSec: 1.45 } # sunset split_layer { layerId:"v5", atSec: 1.50 } # waterfall set_layer_transition { layerId:"v3", out:{ id:"slicewipe", durationSec:0.18 } } set_layer_transition { layerId:"v5", out:{ id:"zoomBlur", durationSec:0.20 } } # Look — global cinematic grade, then signature LUT (1=neutral on hue/sat/contrast) apply_global_color_grade { hue: 6, contrast: 1.10, saturation: 1.18, temperature: -0.2 } set_layer_lut { layerId:"v3", id:"teal-orange", intensity: 0.6 } # Titles & lower thirds add_text_layer { text:"BALI", startTime: 0.2, duration: 2.2, fontSize: 96, verticalAnchor:"center", fontWeight:"900" } set_text_animation { layerId:"text0", inId:"in-scale-up", inDurationSec: 0.45, outId:"out-fade-up", outDurationSec: 0.35 } add_lower_third_layer { presetId:"bold-pop", lines:["Day 2 — Ubud"], startTime: 18.0, duration: 3.0 } # On-device captions for any future VO add_caption_layer_from_audio { uri:"vo.wav", language:"auto", x: 50, y: 84 } # Shape-aware opening mask — real keyframes, not a preview-only effect create_mask_animation { layerId:"v0", shape:"ellipse", pattern:"ellipse:sample:reveal", curve:"easeOut" } # Mix — fade music out on the last beat set_layer_volume_keyframes { layerId:"music", keyframes:[ {timeMs:0, volume:0.85}, {timeMs:27000,volume:0.85}, {timeMs:30000,volume:0.0} ]} # Review the motion and compare canvas with the encoded result preview_filmstrip { fromSec:0, toSec:30, frames:6, maxWidth:512 } capture_canvas { timeSec:1.5, grid:true } verify_export_parity { timesSec:[1.5,15,28] } save_project_as_template { id:"bali-reel", name:"Bali Reel", mode:"template" } # Export set_export_settings { resolution:"1080x1920", fps: 30 } export_project {}
"Launch a premium product film with a masked title card and a portable brand look."
Animate the title mask with an explicit curve, keep the logo card's mesh fill and shader backdrop in the same scene graph, then bake the brand CDL/LUT. Verify the title at its transition times before exporting a snapshot template.
# Intake Q1: Platform? → YouTube pre-roll, 16:9 1080p 24fps (film cadence) Q2: Brand palette? → Espresso brown, cream, gold Q3: Length cap? → 60s hard, with 15s and 30s cutdowns later # Structure Act I (0–12s): origin — slow drone over coffee terraces, brand title card. Act II (12–42s): craft — close-ups of hands, pour, steam, roasted bean macro. Act III(42–60s): payoff — finished cup in hand, logo reveal, tagline, URL. # Plan — 26 calls, film-style 24fps create_project { name:"Origin — 60", aspectRatio:"16:9", fps:24 } add_video_layer × 6 # 6 hand-picked hero shots → video0..video5 add_stock_music_layer{ soundId: 8842, startTime:0, duration:60, volume:0.4 } # Letterbox bars — flat black rectangles, canvas-relative so they hold at any export size add_shape_layer { shape:"rectangle", fillColor:"#000000", x:0, y:0, canvasRelativeWidth:100, canvasRelativeHeight:12, startTime:0, duration:60 } add_shape_layer { shape:"rectangle", fillColor:"#000000", x:0, y:88, canvasRelativeWidth:100, canvasRelativeHeight:12, startTime:0, duration:60 } # Slow push-ins on every hero shot (24fps cinematic cadence, ~7.8s clips) for clipId in [video0..video5]: keyframe_add { layerId:clipId, property:"transform.scale", timeMs:0, value:1.00 } keyframe_add { layerId:clipId, property:"transform.scale", timeMs:7800, value:1.07, interp:{type:"preset",name:"easeInOut"} } # Title card — a shape with an animated MESH gradient fill, not a flat color add_shape_layer { shape:"rectangle", x:0, y:0, stretchToCanvas:true, startTime:8.0, duration:4.0 } set_shape_fill { layerId:"shape2", style:"mesh", meshPresetId:"sunset" } add_text_layer { text:"ORIGIN", startTime:8.0, duration:4.0, fontSize:120, verticalAnchor:"center", fontFamily:"Serif Display" } # One-call mask graph — geometry, opacity, and curve stay in one auditable animation create_mask_animation { layerId:"text0", shape:"rectangle", curve:"easeInOut", keyframes:[ {timeMs:0, mask:{rect:{x:0,y:0,width:0,height:1}, feather:2}, opacity:0}, {timeMs:900, mask:{rect:{x:0,y:0,width:1,height:1}, feather:2}, opacity:1}, {timeMs:3200, mask:{rect:{x:0,y:0,width:1,height:1}, feather:2}, opacity:1}, {timeMs:4000, mask:{rect:{x:0,y:0,width:1,height:1}, feather:2}, opacity:0} ] } # Brand LUT — bake a CDL grade from the reference still, register, apply globally bake_cdl_to_cube { cdl:{ slope:[1.02,0.98,0.93], offset:[0.02,0.01,-0.02], power:[1.05,1.0,1.08], saturation:0.92 }, size:33 } lut_register_custom { id:"origin-warm", name:"Origin Warm", cubeText:"<cube text>" } apply_global_color_grade { lutId:"origin-warm", lutIntensity:0.78 } # VO + manual music duck under the line tts_validate_script { text:"Some things take time. Patience. Heat. A hand that knows.", defaultVoice:"am_michael" } tts_add_audio_layer { text:"Some things take time. Patience. Heat. A hand that knows.", voiceId:"am_michael", startTime:14.0 } set_layer_volume_keyframes { layerId:"music", keyframes:[ {timeMs:14000,volume:0.40},{timeMs:14400,volume:0.18}, {timeMs:30000,volume:0.18},{timeMs:30400,volume:0.40} ]} # Logo reveal end card — scale-pop on a generative shader backdrop add_generative_bg_layer { effectId:"silk", preset:"Mercury", startTime:54.0, duration:6.0, opacity:0.6 } add_image_layer { uri:"asset://logo.png", startTime:54.0, duration:6.0, x:30, y:30, stretchToCanvas:false } keyframe_add { layerId:"image0", property:"transform.scale", timeMs:0, value:0.0, interp:{type:"preset",name:"easeOut"} } keyframe_add { layerId:"image0", property:"transform.scale", timeMs:480, value:1.06, interp:{type:"preset",name:"easeIn"} } keyframe_add { layerId:"image0", property:"transform.scale", timeMs:620, value:1.0 } set_layer_border_glow { layerId:"image0", color:"#d4a056", intensity:60, radius:14 } # Check the title transition and full end card, then save the portable brand recipe capture_canvas { timeSec:8.6, xray:true } preview_filmstrip { fromSec:7.5, toSec:60, frames:6 } verify_export_parity { timesSec:[8.6,56] } save_project_as_template { id:"origin-brand", name:"Origin Brand Spot", mode:"snapshot" } # Export — film cadence set_export_settings { resolution:"1920x1080", fps:24, quality:"high" } export_project {}
"Cut a three-minute documentary with on-device captions, dialogue cleanup, and loudness delivery."
Keep interview language intact, clean the dialogue on-device, generate time-aligned captions, and measure the final mix against a named loudness target. Use canvas inspection to catch lower-thirds that drift outside the safe area.
# Intake Q1: Aspect & length? → YouTube 16:9 1080p 30fps, 3:00 hard cap Q2: Subject lang? → Hindi interview — on-device Whisper transcribes Hindi (no translation pass; captions stay in the spoken language) Q3: Tone? → Warm, observational, no music under speech # Structure 00:00–00:18 Cold open — wide shot of stall + ambient sound, no VO 00:18–00:32 Lower-third intro: "Ramesh · Vada Pav · Dadar" 00:32–02:20 Interview A-roll with B-roll over the long answers 02:20–02:48 Cooking montage with ambient music swell 02:48–03:00 Tail card + URL # Plan create_project { name:"Ramesh", aspectRatio:"16:9", fps:30 } add_video_layer { uri:"interview.mp4", startTime:0, duration:180 } # On-device transcript + captions — keep the interview language intact transcribe_audio { uri:"interview.mp4", language:"hi", modelSize:"base" } add_caption_layer_from_audio { uri:"interview.mp4", language:"hi", x:50, y:86 } clean_audio { layerId:"video0", strength:0.7 } add_audio_layer { uri:"ambient_market.wav", startTime:0, duration:180, volume:0.25 } add_stock_music_layer { soundId: 5510, startTime:140, duration:40, volume:0.0 } # Music swell under cooking montage set_layer_volume_keyframes { layerId:"audio1", keyframes:[ {timeMs:0, volume:0.0},{timeMs:4000,volume:0.45}, {timeMs:28000,volume:0.45},{timeMs:32000,volume:0.0} ]} # B-roll cutaways over the long answers add_video_layer { uri:"b-roll-pour.mp4", startTime:45, duration:8 } add_video_layer { uri:"b-roll-spice.mp4", startTime:72, duration:7 } add_video_layer { uri:"b-roll-hands.mp4", startTime:110, duration:9 } add_video_layer { uri:"b-roll-crowd.mp4", startTime:135, duration:5 } # A-roll audio stays under each B-roll cutaway — that's the rule. # Lower third intro add_lower_third_layer { presetId:"documentary-thin", lines:["Ramesh · Vada Pav · Dadar"], startTime:18.0, duration:5.0, x:18, y:82 } # Chapter breaks — dip to black is a classic VISUAL transition on the cut, not an audio edge set_layer_transition { layerId:"video0", out:{ id:"dipToBlack", durationSec:0.6 } } # On-device captions — transcribed straight off the interview audio (Hindi stays Hindi) add_caption_layer_from_audio { uri:"interview.mp4", language:"hi", modelSize:"base", x:50, y:86 } # Vintage doc grade — slightly faded, warm (multipliers around 1.0 = neutral) apply_global_color_grade { contrast:1.18, saturation:0.88, temperature:0.05 } set_layer_lut { layerId:"video0", id:"bleach-bypass", intensity:0.5 } # Tail card add_text_layer { text:"Find Ramesh — Dadar West, 6am – 10am", startTime:170, duration:10, fontSize:36, verticalAnchor:"center", fontWeight:"500" } set_text_animation { layerId:"text1", inId:"in-fade-up", inDurationSec:0.8 } # Loudness — broadcast spec (per-clip measure, then a normalization plan) measure_loudness { samplesBase64:"<float32 mono PCM>", sampleRate:48000 } build_loudness_plan { rows:[{ clipId:"a-roll", name:"Interview", measurement:{...} }], target:"broadcastEbu" } # Inspect lower-third bounds, caption timing, and export parity before delivery describe_canvas { timeSec:18.0 } capture_canvas { timeSec:18.0, xray:true, grid:true } verify_export_parity { timesSec:[18,96,176] } set_export_settings { resolution:"1920x1080", fps:30 } export_project {}
"Make a vertical music video where the mask, grade, and typography follow the track."
Separate bass and vocal stems, drive the chorus from the audio envelope, and author a shape-aware mask graph with named curve presets. Review motion as a filmstrip rather than trusting a single frame, then parity-check the MP4.
# Intake Q1: Aspect & length? → 9:16 1080p 30fps, full single = 2:30 Q2: Sections? → Intro 0:00, Verse 0:18, Chorus 0:48, Verse 0:90, Chorus 0:120, Outro 2:18 Q3: Lyrics on screen? → Hook line only, on every chorus # Plan create_project { name:"Indie Single", aspectRatio:"9:16", fps:30 } add_audio_layer { uri:"single_master.wav", startTime:0, duration:150, volume:1.0 } add_video_layer × 12 # performance shots video0..video11 # Intro (0–18s) — mask reveal of the artist name add_text_layer { text:"AYA", startTime:6.0, duration:6.0, fontSize:200, verticalAnchor:"center", fontWeight:"900" } create_mask_animation { layerId:"text0", shape:"rectangle", pattern:"rectangle:sample:pulse", curve:"flow" } # Real beat-detection — separate the master, find the actual drum + bass hits separate_audio { layerId:"audio0", tier:"studio" } beat_cut_from_drums { sourceLayerId:"audio0", sensitivity:1.0, maxCuts:24 } # → cutTimesMs over the verse + chorus; split each performance clip atSec relative to its own startTime split_layer { layerId:"video1", atSec:0.97 } # verse cut split_layer { layerId:"video6", atSec:0.48 } # chorus cut, punchier # Chorus drop — bass stem drives a zoom-pulse on the performance clip, no manual keyframes set_audio_reactive { targetLayerId:"video6", stemLayerId:"bass_audio0", stem:"bass", presetId:"bass-zoom-pulse", intensity:0.8 } set_audio_reactive { targetLayerId:"video6", stemLayerId:"drums_audio0", stem:"drums", presetId:"drum-wobble-rotate", intensity:0.4 } # Kinetic-typography lyric drop — hook line, one word per beat, vocal stem flashes its opacity words = ["I", "DON'T", "REMEMBER", "ANYMORE"] for i, w in enumerate(words): add_text_layer { text:w, startTime: 48.0 + i*0.484, duration:0.6, fontSize:140, verticalAnchor:"top", y:38, fontWeight:"900" } set_text_animation { layerId:"text"+(i+1), inId:"in-scale-up", inDurationSec:0.18 } set_audio_reactive { targetLayerId:"text"+(i+1), stemLayerId:"vocals_audio0", stem:"vocals", presetId:"vocal-flash-opacity", intensity:0.9 } # Mask-wipe section break at the bridge (96s) — an off-screen shape slides across add_shape_layer { shape:"rectangle", fillColor:"#0a0a0f", stretchToCanvas:true, startTime:96.0, duration:0.6 } keyframe_add { layerId:"shape0", property:"transform.x", timeMs:0, value:-100 } keyframe_add { layerId:"shape0", property:"transform.x", timeMs:600, value:100, interp:{type:"preset",name:"easeInOut"} } # Time-ranged split grade — verses cooler, choruses warmer (apply_global_color_grade scopes by startSec/endSec) apply_global_color_grade { startSec:18, endSec:48, temperature:-0.12, saturation:0.92, contrast:1.10 } apply_global_color_grade { startSec:48, endSec:72, temperature:0.10, saturation:1.18, contrast:1.18 } set_layer_lut { layerId:"video6", id:"teal-orange", intensity:0.55 } # Vignette to focus the eye on the performer set_layer_video_effects { layerId:"video6", effects:[{ effectId:"vignette", intensity:35 }] } # Filmstrip + parity check catch a mask/beat mismatch before the final render preview_filmstrip { fromSec:44, toSec:56, frames:6 } capture_canvas { timeSec:48.75, outlinesOnly:true } verify_export_parity { timesSec:[6,48.75,96] } set_export_settings { resolution:"1080x1920", fps:30 } export_project {}
"Build a 90-second how-to with inspectable steps, a countdown widget, and safe captions."
Use a widget for the live countdown, keyframe the step badge, and let the agent describe and capture the canvas at each chapter. The verification pass checks text bounds, timing, and the final vertical export.
# Intake — fast for a tutorial Q1: Steps? → 6 steps, ~12-15s each Q2: Voice? → No VO — text-on-screen with ambient kitchen sounds # Plan create_project { name:"Pour-Over 90s", aspectRatio:"9:16", fps:30 } add_video_layer × 6 # video0..video5 — one shot per step add_audio_layer { uri:"kitchen_amb.wav", duration:90, volume:0.5 } add_stock_music_layer { soundId: 6190, duration:90, volume:0.35 } # Step counter — animated number badge, top-right safe for i in 1..6: t0 = (i-1) * 15 # every 15s add_text_layer { text: i+"/6", startTime:t0, duration:14.5, fontSize:42, x:80, y:8, fullWidth:false, fontWeight:"800" } set_layer_border { layerId:"text"+i, width:3, color:"#ffffff", cornerRadius:24 } set_text_animation { layerId:"text"+i, inId:"in-scale-up", inDurationSec:0.3, outId:"out-fade", outDurationSec:0.25 } # Instruction kinetic text — pop on each step start, typewriter reveal instructions = ["RINSE THE FILTER", "ADD 18g COFFEE", "BLOOM 30s · 50g WATER", "POUR TO 150g", "POUR TO 300g", "WAIT · DRINK · ENJOY"] for i, line in enumerate(instructions): t0 = i * 15 add_text_layer { text:line, startTime:t0+0.3, duration:3.5, fontSize:56, verticalAnchor:"top", y:18, fontWeight:"700" } set_typewriter { layerId:"text"+(i+7), charDelayMs:42 } set_text_animation { layerId:"text"+(i+7), outId:"out-slide-up", outDurationSec:0.4 } # Countdown widget for the 30s bloom step (starts at 32s) add_widget_layer { widgetId:"countdown", startTime:32.0, duration:30, config:{ countdownFromMs:30000, fontSize:120, textColor:"#fbbf24", fontWeight:"bold" } } # Clean daylight grade — bright, accurate, slight warmth (multipliers around 1.0 = neutral) apply_global_color_grade { brightness:1.10, contrast:1.05, saturation:1.10, temperature:-0.05 } # Light music swell on the last step set_layer_volume_keyframes { layerId:"audio1", keyframes:[ {timeMs:0, volume:0.35}, {timeMs:75000,volume:0.55}, {timeMs:90000,volume:0.0} ]} # Inspect each chapter before export — bounds and timing are part of the plan describe_canvas { timeSec:15 } capture_canvas { timeSec:45, grid:true } preview_filmstrip { fromSec:0, toSec:90, frames:6 } verify_export_parity { timesSec:[15,45,75] } set_export_settings { resolution:"1080x1920", fps:30 } export_project {}
"Shape a 75-second wedding highlight with clean vows, gentle motion, and a keepsake template."
Isolate and boost the vows, auto-duck the music, apply a warm look, and use a soft heart or ellipse mask for the rings detail. Save a full-fidelity snapshot locally; only submit it to Community after explicit approval.
# Intake Q1: Aspect & length? → 16:9 1080p 24fps (cinematic), 75s Q2: Music? → "Something soft, piano, no lyrics" Q3: VO? → Use a 25-second slice of the bride's vows, recorded on a phone mic Q4: Names & date? → "Sara & Adam · June 14, 2026" # Structure (emotional arc, not beats) 00:00–00:12 Morning prep — soft details, dress, rings 00:12–00:30 Ceremony walk + kiss (slow-mo on the kiss) 00:30–00:55 Vows VO over wide reception shots 00:55–01:15 Dance + party + tail card # Plan create_project { name:"Sara & Adam", aspectRatio:"16:9", fps:24 } add_video_layer × 18 # curated highlights video0..video17 add_audio_layer { uri:"bride_vows.wav", startTime:30, duration:25 } add_stock_music_layer{ soundId: 9021, startTime:0, duration:75, volume:0.45 } # Dialogue boost — separate the phone-mic vows, raise vocals, duck room noise isolate_voice { layerId:"audio0", boostDb:7, tier:"studio" } # Sparkle Lottie on the rings detail (around 6s) add_lottie_layer { presetId:"sparkles", startTime:5.5, duration:2.5, x:50, y:50 } # Speed-ramp into the kiss set_layer_speed { layerId:"video5", speed:0.5, keepPitch:true } keyframe_add { layerId:"video5", property:"transform.scale", timeMs:0, value:1.0 } keyframe_add { layerId:"video5", property:"transform.scale", timeMs:1200, value:1.12, interp:{type:"preset",name:"easeInOut"} } keyframe_add { layerId:"video5", property:"transform.scale", timeMs:2400, value:1.06 } # Cross-fade transitions between clips in the slow sections set_layer_transition { layerId:"video1", out:{ id:"dissolve", durationSec:0.6 } } set_layer_transition { layerId:"video2", out:{ id:"dissolve", durationSec:0.6 } } # Custom warm-cream LUT (round-trip from a reference still) bake_cdl_to_cube { cdl:{ slope:[1.04,1.02,0.95], offset:[0.02,0.01,-0.01], power:[1.0,1.0,1.04], saturation:0.96 }, size:33 } lut_register_custom { id:"sara-adam-warm", name:"Sara & Adam Warm", cubeText:"<cube text>" } apply_global_color_grade { lutId:"sara-adam-warm", lutIntensity:0.72, brightness:1.10, saturation:1.05, temperature:0.04 } # Auto-duck — music drops automatically wherever the isolated vocal stem is active auto_duck { musicLayerId:"audio1", vocalSourceLayerId:"audio0", duckDb:-14 } # Tail card — names and date with elegant fade add_text_layer { text:"Sara & Adam", startTime:68.0, duration:7.0, fontSize:90, verticalAnchor:"center", fontFamily:"Serif Display", fontWeight:"400" } add_text_layer { text:"June 14, 2026", startTime:68.0, duration:7.0, fontSize:36, y:56, fontFamily:"Serif Display" } set_text_animation { layerId:"text0", inId:"in-fade-up", inDurationSec:1.0 } set_text_animation { layerId:"text1", inId:"in-fade-up", inDurationSec:1.0 } keyframe_add { layerId:"text0", property:"opacity", timeMs:6000, value:1.0 } keyframe_add { layerId:"text0", property:"opacity", timeMs:7000, value:0.0, interp:{type:"preset",name:"easeIn"} } # Rings detail — a soft ellipse sample with a stored easing curve create_mask_animation { layerId:"video2", shape:"ellipse", pattern:"ellipse:sample:drift", curve:"easeInOut" } preview_filmstrip { fromSec:4.5, toSec:8, frames:4 } verify_export_parity { timesSec:[6,35,70] } set_export_settings { resolution:"1920x1080", fps:24, quality:"high" } export_project {}
"Cut a 45-second sports hype reel with drum-locked edits and a mask punch on the drop."
Use onset detection for the cut rhythm, then combine a glitch burst with a mask animation using bounce or elastic easing. Inspect the score card in x-ray mode, confirm the curve has two keyframes, and verify the rendered parity.
# Intake Q1: Platform? → IG Reel + TikTok cross-post, 9:16 1080p 30fps Q2: Energy? → "Maximum. Like a hype train hit a building." Q3: Music? → trap-style instrumental, no vocals create_project { name:"Wildcats Playoff", aspectRatio:"9:16", fps:30 } add_video_layer × 16 # video0..video15 — dunks, blocks, threes, crowd add_stock_music_layer { soundId: 7734, startTime:0, duration:45 } # Real onset detection on the drum stem — punchier than fixed BPM math separate_audio { layerId:"audio0", tier:"fast" } beat_cut_from_drums { sourceLayerId:"audio0", sensitivity:0.8, maxCuts:30 } # → cutTimesMs drives every split below (atSec relative to each clip's own startTime) split_layer { layerId:"video0", atSec:0.43 } split_layer { layerId:"video1", atSec:0.43 } # ...repeat split_layer per returned cut through the verse and the double-time chorus # Drop hits — glitch burst + zoom punch on the dunk clip set_layer_effect { layerId:"video6", effectId:"glitch", params:{ amount:0.6 } } keyframe_add { layerId:"video6", property:"fx.intensity", timeMs:0, value:0.6 } keyframe_add { layerId:"video6", property:"fx.intensity", timeMs:200, value:0.0 } keyframe_add { layerId:"video6", property:"transform.scale", timeMs:0, value:1.0, interp:{type:"preset",name:"easeIn"} } keyframe_add { layerId:"video6", property:"transform.scale", timeMs:120, value:1.25 } keyframe_add { layerId:"video6", property:"transform.scale", timeMs:430, value:1.0 } set_layer_transition { layerId:"video6", in:{ id:"flash", durationSec:0.08 } } # Chorus whip-pans on the punchiest hits set_layer_transition { layerId:"video8", out:{ id:"wipeLeft", durationSec:0.12 } } set_layer_transition { layerId:"video11", out:{ id:"zoomBlur", durationSec:0.14 } } # Big text — "WILDCATS" punch on drop add_text_layer { text:"WILDCATS", startTime:6.7, duration:1.4, fontSize:180, verticalAnchor:"center", fontWeight:"900" } set_layer_border_glow{ layerId:"text0", color:"#ef4444", intensity:85, radius:24, mode:"pulse" } keyframe_add { layerId:"text0", property:"transform.scale", timeMs:0, value:0.0 } keyframe_add { layerId:"text0", property:"transform.scale", timeMs:120, value:1.3, interp:{type:"preset",name:"easeOut"} } keyframe_add { layerId:"text0", property:"transform.scale", timeMs:240, value:1.0 } keyframe_add { layerId:"text0", property:"transform.rotation", timeMs:0, value:-5 } keyframe_add { layerId:"text0", property:"transform.rotation", timeMs:240, value:0 } # Moody high-contrast grade apply_global_color_grade { contrast:1.30, saturation:0.90, temperature:-0.15, brightness:0.95 } set_layer_lut { layerId:"video6", id:"bleach-bypass", intensity:0.55 } # Sponsor lower-third (last 5s) add_lower_third_layer { presetId:"sport-aggressive", lines:["@ HighSchool Gym", "Fri 7pm"], startTime:40, duration:5 } # Final score card — one-tap neon shape style instead of hand-tuned glow values add_shape_layer { shape:"rounded_rect", fillColor:"#000000", startTime:42, duration:3, canvasRelativeWidth:70, canvasRelativeHeight:22, x:15, y:39, cornerRadius:18 } set_shape_style { layerId:"shape0", styleId:"neon-pink" } add_text_layer { text:"WILDCATS 78 — TIGERS 64", startTime:42, duration:3, fontSize:50, verticalAnchor:"center", fontWeight:"900" } # Mask punch — Mirror H sample with bounce easing on the drop create_mask_animation { layerId:"video6", shape:"mirror", pattern:"mirror:sample:pulse", curve:"bounce", mirrorAxis:"horizontal" } capture_canvas { timeSec:7.0, xray:true } preview_filmstrip { fromSec:6.5, toSec:8.5, frames:5 } verify_export_parity { timesSec:[7,24,42] } set_export_settings { resolution:"1080x1920", fps:30 } export_project {}
"Create a 60-second property walkthrough with room-safe reveals and a shareable listing template."
Use a diagonal or mirror mask per room, soft edge fades for the footage, animated labels, and a measured warm grade. Check every reveal with a filmstrip, export the listing, then offer a sanitized Community template with its thumbnail and demo.
# Intake Q1: Aspect? → 16:9 1080p 30fps for MLS + Instagram Q2: Tone? → "Quiet luxury — let the house speak" Q3: CTA? → Agent name + phone number + open-house date # Structure — 7 rooms × ~8s each + 4s opening + 4s closing 00:00–00:04 Aerial drone — front of house with title overlay 00:04–00:12 Entry / great room 00:12–00:20 Kitchen 00:20–00:28 Primary suite 00:28–00:36 Pool deck 00:36–00:44 Wine cellar 00:44–00:52 Sunset terrace 00:52–01:00 Price card + agent CTA # Plan create_project { name:"Malibu Listing", aspectRatio:"16:9", fps:30 } add_video_layer × 7 # video0..video6, one per room add_stock_music_layer { soundId: 3387, duration:60, volume:0.5 } # Slow push-ins everywhere — the room should feel like it's revealing itself for clipId in [video0..video6]: keyframe_add { layerId:clipId, property:"transform.scale", timeMs:0, value:1.00 } keyframe_add { layerId:clipId, property:"transform.scale", timeMs:7800, value:1.05, interp:{type:"preset",name:"easeInOut"} } # Shader-driven wipe between rooms — clean diagonal sweep on every cut for clipId in [video0..video5]: set_layer_transition { layerId:clipId, out:{ id:"slicewipe", durationSec:0.55 } } # Animated room labels — clean serif, slide-in from left, hold, fade labels = ["GREAT ROOM","KITCHEN","PRIMARY SUITE","POOL DECK","WINE CELLAR","SUNSET TERRACE"] for i, l in enumerate(labels): t0 = 4 + i*8 add_text_layer { text:l, startTime:t0+0.4, duration:6.0, fontSize:48, x:8, y:86, fontFamily:"Serif Display", fontWeight:"300", textAlign:"left", fullWidth:false } keyframe_add { layerId:"text"+i, property:"transform.x", timeMs:0, value:-30 } keyframe_add { layerId:"text"+i, property:"transform.x", timeMs:600, value:8, interp:{type:"preset",name:"easeOut"} } keyframe_add { layerId:"text"+i, property:"opacity", timeMs:5400, value:1.0 } keyframe_add { layerId:"text"+i, property:"opacity", timeMs:6000, value:0.0, interp:{type:"preset",name:"easeIn"} } # Price card — Pattern-fill (gold foil texture) border instead of a flat stroke add_shape_layer { shape:"rounded_rect", fillColor:"#1a1a1a", startTime:52, duration:8, canvasRelativeWidth:60, canvasRelativeHeight:40, x:20, y:30, cornerRadius:16 } set_shape_outline { layerId:"shape0", color:"#d4a056", width:2, align:"outside" } set_shape_glow { layerId:"shape0", color:"#d4a056", mode:"breathe", intensity:40, radius:10, placement:"outer" } add_text_layer { text:"$12,450,000", startTime:52.5, duration:7.5, fontSize:72, verticalAnchor:"center", y:42, fontFamily:"Serif Display", fontWeight:"300" } add_text_layer { text:"5 BD · 7 BA · 8,400 SF", startTime:52.5, duration:7.5, fontSize:30, y:52, fontWeight:"400" } add_lower_third_layer { presetId:"elegant-thin", lines:["Maya Linden · Coldwell Banker", "(310) 555-0182 · Open Sat 1–4pm"], startTime:54, duration:6 } # Soft warm grade (multipliers around 1.0 = neutral) apply_global_color_grade { brightness:1.10, contrast:1.08, saturation:1.08, temperature:0.08 } # Opening title — mask-wipe reveal add_text_layer { text:"22810 Pacific Coast Highway", startTime:0.5, duration:3.5, fontSize:52, verticalAnchor:"center", fontFamily:"Serif Display", fontWeight:"300" } create_mask_animation { layerId:"text6", shape:"rectangle", pattern:"rectangle:sample:reveal", curve:"easeOut" } # Room reveal review + community handoff (private media stripped by default) set_layer_fade_mask { layerId:"video0", mode:"inset", softness:0.24 } preview_filmstrip { fromSec:0, toSec:60, frames:8 } verify_export_parity { timesSec:[1,28,56] } save_project_as_template { id:"malibu-listing", name:"Malibu Listing", mode:"snapshot" } set_export_settings { resolution:"1920x1080", fps:30 } export_project {} # use the returned MP4 path for the Community demo submit_template { id:"malibu-listing", name:"Malibu Listing", category:"real-estate", stripPrivateMedia:true, thumbnailUri:"file:///tmp/malibu-thumb.jpg", previewVideoUri:"file:///tmp/malibu-demo.mp4" }
Each plan is a complete tool composition — an agent could send these calls to a connected ExpoCut MCP server and the listed render would appear on the device. Mix and match: a wedding cut can borrow the music-video lyric overlay; a tutorial can borrow the brand-spot end-card reveal. Tip: use ← / → arrow keys to flip between examples.
Conversation starters for assistants.
"Help me cut a Reel"
A guided flow: pick clips, choose a vibe, set duration, auto-suggest transitions and music.
"Make this look cinematic"
Recommend a LUT, set 2.39:1 letterbox, apply teal/orange grade, slow the timeline 12%.
"Add captions"
Suggest a caption style, font, position, and timing for the spoken audio in the clip.
"Trim to 30 seconds"
Identify the strongest beats and propose cuts that fit the target duration.
Run commands from your browser.
Connect to the MCP server running on your phone and execute tool calls directly — no CLI needed. Paste the address and token from ExpoCut → Settings → AI Integration.
Questions, answered.
The essentials about the connector, privacy, and how agents stay current.
Does ExpoCut have a Model Context Protocol (MCP) server?
What can an AI agent actually do with it?
describe_canvas, capture_canvas, preview_filmstrip); and export up to 4K — every step undoable.How does an AI assistant connect to ExpoCut?
How do agents discover the current capabilities?
list_effects, list_shaders, list_transitions, list_shapes, list_fonts, get_effect_schema, get_layer_schema — for the live catalogs.Is it free, and where does the work run?
Live, on the device.
ExpoCut ships an in-app MCP server. When the user enables it, an MCP-compatible client like Claude Desktop connects to a local port on the device and can call the tools above. The server is off by default, accepts requests only from private or loopback network addresses, and every call is recorded as an undoable command in the on-device edit-history log. This page is the public reference for that surface so agents can index it and reason about the app.
Build with ExpoCut.
Download the app, or read the matching Skill for AI assistants.
Join the beta View Skill →