Communitygithub.com

akdavid4real/effectcraft-motion

Build, fix or audit motion-graphics videos (promos, explainers, brand films, logo stings, 9:16 cuts) in EffectCraft, the After Effects-style compositor, with voiceover, music and SFX in sync. Use this whenever the user mentions EffectCraft, an .ecproj file, effectcraft-cli, or asks for a motion graphic / promo video / animated explainer for a brand or website on this machine - including fixing or reviewing an existing EffectCraft project made by another agent - even if they don't name the tool.

What is effectcraft-motion?

effectcraft-motion is a Claude Code agent skill that build, fix or audit motion-graphics videos (promos, explainers, brand films, logo stings, 9:16 cuts) in EffectCraft, the After Effects-style compositor, with voiceover, music and SFX in sync. Use this whenever the user mentions EffectCraft, an .ecproj file, effectcraft-cli, or asks for a motion graphic / promo video / animated explainer for a brand or website on this machine - including fixing or reviewing an existing EffectCraft project made by another agent - even if they don't name the tool.

Works with✓Claude Code✓Codex CLI~Cursor✓OpenCode
npx skills add akdavid4real/effectcraft-motion

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

EffectCraft motion kit

A tested workflow for making 60-second corporate motion graphics in EffectCraft that look designed, are in sync, and are free of the classic bugs: empty frames between scenes, overlapping voice lines, text covered by decorations, numbers that vanish, and SFX off the beat.

Design comes from looks and layouts, not from scratch. Pick one of five tested looks, write each scene as one scene() call with your content and word timings, and the layout handles size, grid, colour and motion. Agents that invent a style end up with dark gradients, glow blobs and small centred text; that is the single biggest reason a generated promo looks cheap.

Any agent can use this. Everything runs from a shell (effectcraft-cli through scripts/ec.py). MCP is optional. Kit location: the folder containing this file (call it $KIT).

Not this kit: stick-figure / character story videos (split-screen "wrong way vs right way") use the stick-figure-story skill instead.

Golden rules (the reasons are in references/)

  1. Times come from data, not guesses. The scene plan lives in timeline.json. Scene scripts use SC("s3") and W("s3", "word") (the absolute time a word is spoken). Never type a time like 23.6.
  2. Look at every scene you build. Render preview frames (ec.py frames) and open the PNGs. Code that runs without errors can still render wrong.
  3. "Done" means the gates passed. timeline.py check has 0 FAIL, ec.py qa has 0 FAIL, ec.py qa-video has 0 FAIL, and you have looked at the contact sheet. Report the results honestly, warnings included.
  4. Use a look and the layouts (references/looks.md). Compare two looks before choosing. At least a third of the scenes must be visual (phone / icons / flow / bars, or a custom scene with an object). Custom scene code is for what no layout covers (a map, a product mock-up), and it still uses the look's palette P(S.bg) and type sizes LOOK.type.
  5. Read references/pitfalls.md before writing any EffectCraft script. Read references/motion-design.md, references/looks.md and references/techniques.md (beat grid, peak-aligned SFX, continuous morphs, ban list) before designing scenes.
  6. The brand is not yours to edit (references/brand.md). Show the real logo file, never the name typed as a wordmark. Write the tagline character for character. Invent no product names, button labels or stats. Fill in the "brand" block in timeline.json so ec.py qa checks this.
  7. Passing QA is not the same as good design. Plan the colour script, the visuals and the signature moment before writing scenes, and score the film in a creative-director review before rendering (references/design-review.md, compared with references/benchmark_sheet.png). 7b. Real product UI (SaaS, app films): follow references/ui-footage.md: capture sample data only, crop to one panel, show it readable (55%+ width) or rebuild it as animated UI, one screen per shot, no tinted walls of thumbnails, no invented numbers, and build here so the QA gates run.
  8. High-energy / hyper-kinetic ads (TikTok-style, a slam on every spoken word): follow references/high-energy.md and templates/hyper.jsx instead of the calm layouts.
  9. Report what the render shows, not what you planned. Before writing the report or handoff, pull frames from the final MP4 (not the preview) at each scene's middle and last half-second, and check every claim: is each line on screen long enough to read, is every drawn step really visible, is the screenshot legible, does the length match the brief and the file name? Read every voice transcript against the script, and listen to the brand name. In testing, an agent's handoff described four diagram steps, three promises and "BridgeAxis Technologies"; the film showed the last step 0.3 s before the cut, the promises for half a second, unreadable screenshots, and a voice saying "Bridge Access". timeline.py check and ec.py qa now warn about unread voice mistakes and text readable for under min_read seconds: fix those warnings, don't explain them away. Run on the highest reasoning setting your agent has: the sloppy run was on a "Low" one.

Project layout

<project>/
  timeline.json        scene plan + sfx cues + music (start from $KIT/templates/timeline.example.json)
  audio_meta.json      voice files + word timestamps (from the audio step)
  soundtrack.wav       generated by timeline.py mix
  assets/voice|sfx|bgm|icons|fonts
  scripts/lib.jsx      copy of $KIT/templates/lib.jsx (set COMP; the look supplies colours and fonts)
  scripts/look.jsx     written by `ec.py look NAME --accent HEX`
  scripts/timing.jsx   generated (timeline.py emit) - never edit
  scripts/wipes.jsx    generated (timeline.py emit) - run LAST
  scripts/reset.jsx, s1.jsx ... s8.jsx
  qa/                  previews and contact sheets

Run every command from <project>/. ec.py reads the project path and comp name from timeline.json.

Workflow A: new video

0. Brief. Ask before building: brand/website to match, mood (loud / calm / technical / luxurious), length and aspect ratio, voice (gender/tone), and whether music/SFX are wanted. Guessing the creative direction is the most common reason a first cut gets rejected.

1. Setup

mkdir -p <project>/{scripts,assets/{voice,sfx,bgm,icons},qa} && cd <project>
cp $KIT/templates/lib.jsx $KIT/templates/reset.jsx $KIT/templates/s1.jsx scripts/
cp $KIT/templates/timeline.example.json timeline.json   # edit project/comp/output names
python3 $KIT/scripts/ec.py fonts                         # installs the looks' fonts (once per machine)

Set COMP in scripts/lib.jsx to the timeline's comp name. Note the brand's accent colour (logo, website CSS, product). If the brand has a website, read its CSS for the accent and fonts. Put the logo files in assets/logo/ and fill in the "brand" block in timeline.json: name, exact tagline, logo files, and any approved product names or URLs (references/brand.md).

2. Script and voice. Write 8 lines (hook, problem, brand, 2–3 benefits, proof, end card); 60 s holds about 110–130 spoken words. Generate one audio file per line and get word timestamps into audio_meta.json in this shape:

{"voices": [{"id": "s1", "path": "assets/voice/s1.wav", "duration_s": 6.45,
             "words": [{"word": "Every", "start": 0.30, "end": 0.59}, ...]}]}

First run python3 $KIT/scripts/ec.py doctor. It lists what this machine has and how to install anything missing. When it says ready, don't research or install alternatives (edge-tts, Whisper, piper, Playwright): the tools below are the tested ones. With no voice at all, time scenes with "in+0.5" offsets instead of words; $KIT/demo/ is a complete silent example (ec.py demo renders it).

  • Voice (the default): local Kokoro, female voice af_heart. Free, no account. The model is cached in ~/.cache/hyperframes/tts/ (downloaded once on the first run):
    export HYPERFRAMES_PYTHON=~/.venvs/kokoro/bin/python   # REQUIRED: the venv with kokoro-onnx
    npx hyperframes tts "Line one text." -v af_heart -o assets/voice/s1.wav   # one file per line
    
    Without HYPERFRAMES_PYTHON it fails with "The kokoro-onnx package is not installed". Don't pip-install anything: set the variable.
  • Word timestamps: npx hyperframes transcribe assets/voice/s1.wav --json. It uses the Parakeet model, cached in ~/.cache/hyperframes/parakeet after the first run (engine auto picks it). Parakeet is right for clean TTS voices like Kokoro. Don't pass -e whisper here: hyperframes' Whisper engine (whisper.cpp) is not set up. For real recorded voices with accents, the footage-edit skill uses faster-whisper large-v3-turbo instead (~/.venvs/asr). Copy the words and their start and end times into audio_meta.json.
  • Music and SFX: the media-use audio engine (node <skills>/media-use/audio/scripts/audio.mjs --request audio_request.json --out audio_meta.json --only bgm,sfx). It retrieves from the HeyGen catalog by description, for example "bgm": {"mode": "retrieve", "query": "warm calm piano"}. <skills> is ~/.claude/skills for Claude Code, ~/.agents/skills for Codex/OpenCode/DeepSeek.
  • HeyGen text-to-speech (through the engine or the heygen CLI) is the alternative voice, but the free allowance runs out. When it does, use Kokoro. Music and SFX retrieval also need the heygen CLI signed in (heygen auth login --oauth) unless HEYGEN_API_BASE is set; without it, sfx still answers from media-use's bundled library, and the user can supply their own music file.

Write down which provider and voice you actually used.

3. Timeline

python3 $KIT/scripts/timeline.py beats      # music tempo, beats, bar starts, drop -> beats.json (needs "music")
python3 $KIT/scripts/timeline.py suggest    # proposes scene in/out + voice_at; with beats.json, scenes end on beats
# paste into timeline.json, set each scene's "bg" (light/dark/accent), add sfx cues anchored to words
python3 $KIT/scripts/timeline.py words      # every word with its absolute time
python3 $KIT/scripts/timeline.py check      # MUST be 0 FAIL
python3 $KIT/scripts/timeline.py emit       # writes scripts/timing.jsx + scripts/wipes.jsx
python3 $KIT/scripts/timeline.py mix        # writes soundtrack.wav (-14 LUFS)

Cut on the beat: timeline.py snap moves existing cuts onto beats where the voice still fits; in scenes, BEAT(n), DOWNBEAT(n), nearBeat(t) and T.drop time slams and pops to the music (references/techniques.md). SFX "at" can be a number, "beat:12", "downbeat:3", "drop", "s2:late" (the word "late" in s2's voice; "s2:the#2" = 2nd "the"), "wipe:s3" (the transition into s3), "in:s3" or "out:s3". Add "offset", "vol", "max" (trim length) and "align": "peak" (the sound's loudest moment lands on the anchor: use it for impacts and pops).

4. Plan, then write the scenes. First write the plan in qa/design_review.md (references/design-review.md, part 1-6): the colour script with one line per scene, the visual that shows what each line says, each scene's hero, and the one signature moment. Give each scene a background in timeline.json ("bg": "light" | "dark" | "accent") and re-run timeline.py emit. Alternate them so each cut reads as a new chapter. Use "accent" for the 1-2 loudest moments, usually the emotional peak. Put the brand reveal and end card on the background where the real logo reads best (usually light), not on the accent. Then write scripts/s1.jsx ... sN.jsx, each one scene() call with a layout (specs: top of templates/layouts.jsx; guide: references/looks.md):

// scripts/s2.jsx
scene("s2", "list", { label: "The problem",
  items: [["Late arrivals.", "late"], ["Customs surprises.", "customs"], ["Tracking that tells you *nothing.*", "tracking", "nothing"]],
  punch: ["Too late.", "too"] });

*word* marks the accent part of a line. Times are words from timeline.py words ("word", "word#2", "word+0.3", "in+0.2"), written without punctuation. Layouts grow or shrink text to fill the frame, so write the copy you want and let the layout size it. Keep on-screen lines short: they are headlines, not the voiceover. Only for something no layout covers (a map, a product mock-up), copy $KIT/templates/scene_template.jsx instead.

Make at least a third of the scenes visual. A film that is only words on backgrounds reads as a slide deck, however good the type. Visual layouts put an object on screen that the voice is about: phone (the product on a phone screen: a payment landing, an order, a tracking update), icons (2-4 drawn pictograms with labels), flow (stops joined by a line that fills as each is spoken: customer → phone → bank, warehouse → port → door) and bars (2-4 bars that grow: fees, waiting times, before/after). Pick the scenes where the voice describes a thing or a process, not a claim.

scene("s4", "flow", { label: "How it works", title: ["Banked the *same day.*", "money", "same"],
  nodes: [{ icon: "shop", label: "You sell", at: "sell" }, { icon: "phone", label: "Tapnest", at: "app" },
          { icon: "bank", label: "Your account", at: "account", note: "Same day" }] });

Icons are drawn by the kit (no files): phone card bank cash clock check tick shop bolt chart truck box pin lock user people chat globe send star plane doc. A custom visual scene counts too when its picture/object layers have "VIS" in their names (e.g. "s5 VIS map"); ec.py qa warns when fewer than a third of the scenes are visual. Don't copy layouts.jsx into the project: ec.py loads it from the kit, and a copy freezes the old version.

5. Pick the look (read references/looks.md first). Shortlist two looks that fit the brand in different ways, take the accent from the brand, and compare them on YOUR scenes:

python3 $KIT/scripts/ec.py new
python3 $KIT/scripts/ec.py import soundtrack.wav assets/icons/*.png
python3 $KIT/scripts/ec.py compare editorial swiss --accent FF5B1F   # -> qa/look_compare.png: OPEN it
python3 $KIT/scripts/ec.py look editorial --accent FF5B1F             # apply the winner

6. Build and preview

python3 $KIT/scripts/ec.py run s1 s2 s3 s4 s5 s6 s7 s8 wipes   # s1 creates the comp + soundtrack
python3 $KIT/scripts/ec.py frames 2 4 6.5                      # OPEN qa/frames.png and look at it

Preview two or three moments of every scene. To fix one scene, edit it and run ec.py rebuild sN (clears only that scene, rebuilds it, re-runs the wipes). To rebuild everything (e.g. after changing the look): ec.py run reset s1 ... sN wipes. The look supplies the transition; to replace it, write your own scripts/wipes.jsx without the "GENERATED" header line (emit then leaves it alone).

Custom-scene checklist (scene_template.jsx shows each pattern):

  • background first; every layer pushed into L; lifeAll(L, S.in, S.out) at the end
  • (custom scenes) colours from P(S.bg), sizes from LOOK.type
  • reveals timed with W(); nothing fades out before S.out
  • children parent()ed to their card BEFORE popIn(card)
  • counters use countUp() on ONE text layer
  • decorative ghost text and glows moved under the content with placeBelow()
  • text sizes follow motion-design.md (labels >= 18 px, body >= 26 px)

7. QA and render

python3 $KIT/scripts/ec.py qa          # fix every FAIL; fix each design WARN or say in the report why it is right
python3 $KIT/scripts/ec.py sheet       # OPEN qa/sheet_1.png + qa/sheet_2.png, look at every frame
# creative-director pass: score the 8 review questions in qa/design_review.md
# (references/design-review.md); redesign any scene scoring below 4, then sheet again
python3 $KIT/scripts/ec.py render      # ~5-10 min on CPU; runs qa-video automatically

QA passing means the film works, not that it is good. In testing, a generic film and a mature one both passed QA cleanly; only the design review separates them. Open qa/render_sheet.png. Check the brand reveal and the end card against references/brand.md: real logo, exact tagline, nothing invented. Then report: the file path, the qa and qa-video results, and anything you chose not to fix and why.

8. Deliver. Put every finished video (wide AND vertical) in ONE folder and give the user the full path of each file. In testing, a user could not find a vertical cut that was rendered into its own project folder. Write AGENT_HANDOFF.md in the project: scene table, brand choices, voice provider and voice, music and SFX sources, and the rebuild commands, so the next agent (or you, later) can pick up without re-researching.

Brand assets: getting a usable logo

  • Take the logo from the brand's website or code (<img> sources, public/, assets/).
  • If it has a baked-in white background, key it out to a transparent PNG. If it has an old tagline or other text baked in, crop it off: never show outdated copy.
  • Check the wordmark against every background you plan to put it on. A navy wordmark disappears on a dark-green scene. Then either keep logo scenes on light backgrounds, or use a light version of the logo that the brand already publishes. Don't recolour the logo yourself.
  • assets/logo must be a real folder, not a symlink: ec.py import can't read files through a symlinked folder.

Vertical 9:16 cut (Reels, TikTok, WhatsApp status)

Make it as a copy of the finished wide project, so the voice, timing and soundtrack are reused:

cp -r <project> <project>-vertical && cd <project>-vertical
# rename project/comp/output in timeline.json (e.g. promo-60s-vertical.mp4)
  • scripts/lib.jsx: W_ = 1080, H_ = 1920. In scripts/look.jsx: side margins about 90 px and a content area about y = 440-1640 (keep clear of the app UI at the top and bottom).
  • Re-flow every scene: shorter lines (3-4 words), logo stacked ABOVE the text instead of beside it, rows of icons become columns or 2×2 grids. Layouts built for landscape (e.g. phone) may need the phone centred and scaled up, with the headline under it.
  • Rebuild everything: ec.py run reset s1 ... sN wipes, then QA, sheet and render as usual.
  • Then copy the vertical mp4 next to the wide one (step 8).

Workflow B: fix or audit an existing project

  1. Copy the project before touching it, unless the user said to edit in place.
  2. Write a timeline.json with project, comp, duration and the scenes, including each scene's voice and voice_at copied from wherever the project really places its voice lines (its mix script, or audio_meta.json). Without those, timeline.py check refuses to pass, because it would be checking nothing. Then run timeline.py check, ec.py qa, ec.py qa-video <mp4>, and ec.py sheet. Keep timeline.json in sync with every re-timing you make.
  3. Find the voice offsets (look for a mix script or audio_meta.json) and check them against the word timestamps. Look for overlapping lines, words that spill past a scene cut, and SFX more than 0.3 s away from their word.
  4. Fix the causes in the project's own scene scripts, rebuild the affected scenes, and re-run QA until there are 0 FAIL. If the project has its own build script, keep using it, so you don't end up with two build paths.
  5. Report before/after QA output and what changed. Mention anything left over (e.g. off-brand fonts). Only claim a fix that a QA command or a frame you looked at confirms. In testing, an agent without these checks reported a collision as fixed while it was still on screen.

Using MCP instead of the CLI (optional)

If the EffectCraft GUI is running with --control 9877 and you have the effectcraft-app MCP server, run_script works the same way. Prepend lib.jsx and timing.jsx yourself, and use render_preview for frames. Don't mix the two on one project: the GUI holds the project in memory and overwrites CLI edits when it saves (pitfall #19).

When things go wrong

SymptomLikely causeFix
ERR ... not found in voice lineword typo or different wording in the TTStimeline.py words and use the exact word
scene builds twice / layer count jumpedre-ran after a "timeout"ec.py rebuild sN
text invisibleparented after popIn, or created under its boxpitfalls #6 / #9
wrong-looking fontvariable font or fallbackpitfalls #16–17
wipes hidden behind a scenescene rebuilt after the wipesec.py run wipes (rebuild does this)
qa dead frames at a cutcontent faded out early / next scene starts latekeep content until S.out; first reveal within 0.3 s

Related Skills