Headless motion graphics with EffectCraft (After-Effects-style comps, layers, text presets, keyframes), provisioned on demand by pi-craft - animate a composition and render a transparent ProRes 4444 overlay, mp4, GIF or still. Use for "make an animated title with a transparent background", "render a lower third as a mov with alpha", "export a still frame of this comp".
Install
npx skillscat add blackbelttechnology/pi-agent-dashboard/effectcraft Install via the SkillsCat registry.
EffectCraft — headless motion graphics
EffectCraft is ArtCraft's compositing/motion app (comps, layers, 731 commands, 300+ effects). pi-craft
provisions the headless effectcraft-cli on first use (no desktop app). Choosing between apps, the
discover → execute → save → verify loop, the hand-off formats and the MCP policy live in craft-studio
(an alpha overlay for FilmCraft is mov ProRes 4444 with rgba channels — this skill produces it).
What it is for / formats
Compositions of text, shapes, footage and effects with keyframes, expressions and text animation presets.
Project file: .ecproj. Renders h264 hevc av1 prores webm gif, image sequences png jpeg tiff exr, audiowav aiff; a single still via render-frame (png). For finished video editing use FilmCraft, for a still
raster edit PhotoCraft.
CLI dialect
pi-craft effectcraft exec <args…> passes the arguments to effectcraft-cli.
Project flags come before the subcommand (--empty, --project F.ecproj, --save, --save-as F).
effectcraft-cli commands [--filter TEXT] [--enabled] [--json] # list ids; --json adds the parameter schema
effectcraft-cli exec <id> --params JSON # one command ("exec --list" = commands)
effectcraft-cli run <id> <json> [<id> <json> …] # several, in order (each id followed by its JSON)
effectcraft-cli script FILE.jsx [F.ecproj] | --eval CODE # After-Effects-style JS object model (project, comps, layers)
effectcraft-cli info | props <comp> <layer> [--flat] | get <comp> <layer> <path> [--time S] | set <comp> <layer> <path> <value> [--time S]
effectcraft-cli render [--comp C] --out FILE --format F [--prores 4444] [--channels rgb|rgba] [--resolution half] [--start S --end S]
effectcraft-cli render-frame [--comp C] --time S|--frame N [--max-side PX] --out F.png [--transparent]<comp>: id or name,-= the active comp.<layer>: id,#n(index) or name. Property paths are
slash paths fromprops --flat(e.g.transform/opacity,transform/position).runtakes JSON as the next argument, not--params:run comp.new '{…}' layer.newText '{…}'. Each command
prints its result ({"comp":1},{"layer":2}) — keep ids for later steps.- Text animation presets:
layer.applyTextPreset '{"preset":"fadeUpCharacters"}'(alsotypewriter,blurIn,trackingIn,dropInLines, …); it applies to the last layer whenlayeris omitted. - Keyframes:
prop.addKey '{"layer":"Name","path":"transform/opacity","time":1,"value":100}'.
State between invocations
A bare run/exec/set changes an in-memory project and throws it away on exit. Persist explicitly:
- first time:
--empty --save-as t.ecproj run …(creates the file); - later:
--project t.ecproj --save run …(open, edit, write back).--project t.ecproj run …without--savediscards the edit; arender/render-frame/infoon--projectonly reads. --savealso works onset(--project t.ecproj --save set Title Hello transform/opacity 100 --time 1).
Discovery first
Command ids change between releases and parameters are optional-heavy objects — read the schema.
pi-craft effectcraft exec commands --filter newText # id, label, shortcut, params
pi-craft effectcraft exec commands --json --filter prop.addKey # JSON schema (+ "enabled"/"why")
pi-craft effectcraft exec --project t.ecproj info # comps, layers, active comp
pi-craft effectcraft exec --project t.ecproj props - '#1' --flat # property paths of layer 1 in the active compTraps
- No
--project/--empty⇒ a pre-populated demo project (folders, "Lower Third", 90+ items), not a blank one.
Always start with--empty --save-as X.ecprojso a leftover demo item cannot end up in your render. - Alpha needs both
--format prores --prores 4444and--channels rgba(.mov). Without--channels rgba
the file isyuv444p12leandverify --alphafails; with it the pixel format isyuva444p12leand the
background really is transparent (comp background colour is not painted). render-frameis opaquergb24unless you add--transparent(thenrgba).- A PNG render (
--format png) is a sequence:--out nocomp.pngwritesnocomp_00025.png(frame number
appended). For one frame userender-frame. render-frame --timeis in seconds,--framein frames;--max-side 640/--scale Kshrinks the still.set … --time Tcreates a keyframe at T (the property becomes animated with a single key); omit--time
for a static value. Useprop.addKeypairs for a real animation.pi-craft verify --frame-diff 1.5:still.png\>0.1fails with "no frame at 1.5 s" because a still only has a
frame at 0 — write1.5:still.png@0\>0.1(@<ref time>). The diff compares luma, so a 1 % result for a
title on transparency is normal.- The
--outofrenderis relative to the working directory; the render line prints the absolute path. - Layer names: the text layer is named after its text unless
nameis passed; duplicates makeNameambiguous
— use#nor the returned id.
MCP route (opt-in)
pi-craft effectcraft mcp --register writes a craft-effectcraft entry into .pi/mcp.json (27 tools). It is
confined to the project directory (automation read/write roots; scripts are refused). Use MCP for a long
interactive session on one open document, bash for everything above. GUI bridging (--bridge) is out of scope.
Recipes
# Animated title (fades in over 1 s) -> ProRes 4444 overlay with real transparency.
pi-craft effectcraft exec --empty --save-as t.ecproj run \
comp.new '{"name":"Title","width":1280,"height":720,"frameRate":25,"duration":2}' \
layer.newText '{"text":"Fade","size":120,"position":[640,360]}' \
prop.addKey '{"layer":"Fade","path":"transform/opacity","time":0,"value":0}' \
prop.addKey '{"layer":"Fade","path":"transform/opacity","time":1,"value":100}'
pi-craft effectcraft exec --project t.ecproj render --out title.mov --format prores --prores 4444 --channels rgba
pi-craft effectcraft exec --project t.ecproj render-frame --time 0 --out blank.png --transparent
pi-craft verify title.mov --alpha --size 1280x720 --duration 2±0.1 --frame-diff 1.5:blank.png@0\>0.1# A still from an animated text preset; --max-side halves it, --transparent keeps alpha.
pi-craft effectcraft exec --empty --save-as t.ecproj run \
comp.new '{"name":"Title","width":1280,"height":720,"frameRate":25,"duration":2}' \
layer.newText '{"text":"Hello","size":120,"position":[640,360]}' \
layer.applyTextPreset '{"preset":"fadeUpCharacters"}'
pi-craft effectcraft exec --project t.ecproj render-frame --time 0 --out first.png --transparent
pi-craft effectcraft exec --project t.ecproj render-frame --time 1.5 --max-side 640 --out still.png --transparent
pi-craft verify still.png --alpha --size 640x360# Re-open the saved project with --save to add a layer, then render a half-resolution mp4.
pi-craft effectcraft exec --empty --save-as t.ecproj run \
comp.new '{"name":"Card","width":1280,"height":720,"frameRate":25,"duration":2,"background":"#203040"}' \
layer.newText '{"text":"One","name":"One","size":100,"position":[640,260]}'
pi-craft effectcraft exec --project t.ecproj --save run \
layer.newText '{"text":"Two","name":"Two","size":100,"position":[640,460]}'
pi-craft effectcraft exec --project t.ecproj render --out card.mp4 --format h264 --resolution half
pi-craft verify card.mp4 --size 640x360 --duration 2±0.1 --has-video# The After-Effects-style object model: build a comp from JavaScript, save, render.
cat > build.jsx <<'JS'
var A = app;
var C = A.project.items.addComp("Scripted", 640, 360, 1, 2, 25);
C.layers.addText("Hi");
writeLn(C.name + " " + C.layers.length);
JS
pi-craft effectcraft exec --empty --save-as s.ecproj script build.jsx
pi-craft effectcraft exec --project s.ecproj render-frame --time 0 --out frame.png
pi-craft verify frame.png --size 640x360