[ CREATIVE ]
/motion-ad-generator
Generates a fully finished, export-ready branded video ad using Creatomate — no CapCut step, no manual editing.
ACME Agency, <id> and you@example.com mark values that are per-agency — your install fills them with YOUR clients and accounts. If a section references a helper script you don't have yet, it ships with that workflow's install.Skill: /motion-ad-generator
Overview
Generates a fully finished, export-ready branded video ad using Creatomate — no CapCut step, no manual editing. Canva-style: animated text, brand images (Krea Nano Banana), ElevenLabs voiceover, background music, brand colors.
What this skill produces:
- Finished MP4 (1080×1920, 9:16) — ready to upload directly to Meta/TikTok
- ElevenLabs voiceover (Croatian/German/English)
- 2–3 brand images (Krea Nano Banana)
- All files in Google Drive + Slack report
What this skill does NOT do:
- Generate AI video footage (use
/video-ad-generator --style cinematicfor that) - Require any template setup — Claude writes the full composition JSON per render
When to use:
- Fast-turnaround branded ads (same day delivery)
- Offer announcements, promotions, new service launches
- Variants of existing campaigns (change headline/CTA, keep brand colors)
- When client doesn't need cinematic footage — just wants a clean branded video
Trigger:
/motion-ad-generator ClientName/motion-ad-generator ACME Agency --brief "mini implant offer, 20% popust, seniors"
Critical Files
ACME Agency/scripts/motion_ads_generate.mjs— pipeline scriptACME Agency/scripts/lib/creatomate.mjs— Creatomate API wrapper + buildSource()ACME Agency/scripts/lib/elevenlabs.mjs— ElevenLabs TTSACME Agency/scripts/lib/krea.mjs— Krea image generation (Nano Banana)ACME Agency/scripts/lib/google_drive.mjs— Drive uploadACME Agency/scripts/lib/slack.mjs— Slack reportingACME Agency/clients/clients.json— client registryACME Agency/clients/<ClientName>/brand-dna.md— brand contextACME Agency/clients/<ClientName>/motion-ads/<campaign>/script.json— Claude-written script
Script Format
Write this to ACME Agency/clients/<ClientName>/motion-ads/<campaign-slug>/script.json before running the pipeline:
{
"client": "ClientName",
"campaign": "<id>",
"adType": "motion-graphics",
"targetDuration": 20,
"voScript": "Pune 48 riječi za 20 sekundi. Kalibriraj prema formuli: targetDuration × 2.4 = broj riječi.",
"headline": "Kratki, udarni naslov — max 6 riječi",
"subheadline": "Benefit ili specifikacija (opcionalno)",
"cta": "Zatraži besplatnu konzultaciju",
"brandColors": {
"primary": "#1A4F8A",
"accent": "#D59C44",
"text": "#FFFFFF"
},
"musicMood": "professional",
"imagePrompts": [
"Modern dental clinic interior, Croatia, bright professional photography, no people, wide shot, 9:16 vertical",
"Close-up of healthy white teeth and warm smile, soft studio light, authentic Croatian aesthetic",
"Titanium mini implant screws macro photography, gold accent lighting, clean white background"
]
}
Campaign slug: <keyword>-<offer/audience>-<YYYY-MM> e.g. <id>
VO Calibration — CRITICAL
Same rule as cinematic pipeline:
ElevenLabs Croatian ≈ 2.3–2.5 words/second
Formula: targetDuration × 2.4 = target word count
Example: 20s = 48 words | 25s = 60 words | 30s = 72 words
Count words BEFORE saving script.json. Mismatch = VO ends before video.
Music Moods
| Mood | Use when |
|---|---|
professional | Clinics, B2B, services, authority brands |
energetic | Fitness, youth audience, product reveals |
warm | Lifestyle, family, wellness, emotional appeal |
emotional | Testimonials, transformation stories |
neutral | Luxury, real estate, minimalist brands |
Image Prompts
Write 2–3 prompts. Each image fills the upper ~55% of the 9:16 frame for one phase of the video (hook → benefit → CTA). No text needed in images (text is added by Creatomate). Tips:
- Specify
9:16 verticalin each prompt - Include brand setting (clinic, office, product closeup, lifestyle)
- Avoid people's faces when possible (consistency issue across slides)
- Think: what visual reinforces the headline? → that's image 1
Preflight (run BEFORE any Krea/ElevenLabs/Creatomate call)
Motion ads compose multiple paid services (Krea images + ElevenLabs VO + Creatomate render). One missing piece = whole pipeline wasted. Validate all of them upfront.
- Client exists in
clients.json. Resolve canonical key. - Required env vars:
KREA_API_KEY,ELEVENLABS_API_KEY,CREATOMATE_API_KEY. If any missing → abort, name them. - Script JSON has all required fields:
campaign,headline,cta,voScript,imagePrompts[],brandColors. Missing any → abort. imagePrompts[]length is 1-6 (rendering more is wasted; rendering 0 = no visuals).voScriptword count matchestargetDurationper the calibration table in this SKILL.md (## VO Calibration). If mismatch → abort with the expected range.musicMoodis one of:professional,energetic,warm,emotional,neutral. Reject anything else.brandColors.primaryandbrandColors.accentare valid hex (#your-channel). Required by Creatomate templates.- Croatian/German diacritics check in
voScript— warn if you sees/z/c/dwhereš/ž/č/ć/đshould be (ElevenLabs needs correct chars). drive_folder_idreachable if--driveenabled.- Slack channel resolves if reporting enabled.
- Creatomate image transport: images are inlined as
data:image/jpeg;base64,...URIs viatoDataUri()(fromlib/creatomate.mjs). No external host required. catbox.moe was the prior dependency — it's now HTTP 412 banned (2026-05-26) and 0x0.st is 503 indefinitely. See <id>.md.
If all checks pass, log "preflight: OK (n images, voscript=Xs, music=mood, est cost=...)" and proceed.
Workflow
Step 0 — Client lookup
Read ACME Agency/clients/clients.json. Extract:
drive_folder_id— Drive upload targetslack_channel— Slack report targetelevenlabs_voice_id— default voice (use if set)market— language context (Croatia, Germany, etc.)
Step 1 — Brand research
Check for ACME Agency/clients/<ClientName>/brand-dna.md. Need at minimum:
- Primary hex color, accent hex color
- Brand tone (for music mood selection)
- Language
If brand-dna.md doesn't exist: scrape website via Firecrawl, extract colors and tone, write brand-dna.md.
Step 2 — Brief + script.json
If --brief was provided: use it directly.
If not, ask 4 questions:
- What's this campaign about? (offer, product, key message)
- Target audience?
- Duration? (15s / 20s / 30s) — default: 20s
- Music mood? (show the mood table) — default: professional
Write script.json to motion-ads/<campaign>/script.json.
Show user a summary before running:
Campaign: <id> | Duration: 20s | Music: professional
Headline: "Bezbolni mini implantati — za samo 2 sata"
Subheadline: "Bez rezanja. Bez opće anestezije."
CTA: "Zatraži besplatnu konzultaciju"
VO: 48 words ✓ (20s × 2.4)
Images: 3 prompts
Proceed?
Step 3 — Run the pipeline
node ACME Agency/scripts/motion_ads_generate.mjs "ClientName" \
--script "ACME Agency/clients/ClientName/motion-ads/<campaign>/script.json" \
[--voice <voice_id_or_name>] \
[--no-slack] [--no-drive]
Pipeline phases:
- Phase A — ElevenLabs → voiceover.mp3
- Phase B — Krea Nano Banana → image-01.jpg, image-02.jpg, image-03.jpg
- Phase C — All assets uploaded to Drive → public URLs
- Phase D — Creatomate renders full MP4 → downloaded → uploaded to Drive
- Phase E — Slack report with Drive link + feedback thread
Output
ACME Agency/clients/<ClientName>/motion-ads/<campaign>/
├── script.json
├── voiceover.mp3
├── image-01.jpg
├── image-02.jpg
├── image-03.jpg
├── final-ad.mp4 ← finished, upload-ready
└── manifest.json
Drive: <ClientFolder>/Motion Ads/<Year>/<Month>/<campaign>/
Verification (run AFTER pipeline completes — confirm the MP4 actually shipped)
Check ALL of these before declaring done:
- [ ]
final-ad.mp4exists locally AND file size > 500 KB - [ ]
voiceover.mp3exists locally AND duration ≈ targetDuration (within ±15%) - [ ] Image count locally =
imagePrompts.length(no silent Krea failures dropping images) - [ ] Each image > 50 KB on disk
- [ ] If
--drive:final-ad.mp4,voiceover.mp3, all images uploaded to DriveMotion Ads / <Year> / <Month> / <campaign>/AND public URLs in manifest - [ ]
manifest.jsonrecords: campaign, duration, voice ID, music mood, image count, Creatomate render ID - [ ] Slack post via slack-reporter returned
tsnon-null - [ ] Creatomate render status =
succeeded(notfailedorpendingswallowed) - [ ] No "black screen" failure pattern (verify by checking final MP4 is non-trivial size + Creatomate response had
urlfield)
If any check fails, name the gap explicitly. Most common Creatomate failure mode is silent black screen — check video file size as the canary.
Tips
- Test locally first:
--no-slack --no-driveto check VO and images without Drive upload or Creatomate render - Voice not right? Run
--list-voicesthen pass--voice <id>explicitly - Re-render only: if images and VO are already uploaded, you can re-run just the Creatomate step by passing the existing public URLs directly in the source JSON via
buildSource() - Croatian diacritics: always verify š, đ, č, ž, ć in voScript — ElevenLabs needs correct diacritics for proper pronunciation
- Cost per video: ~$0.27 Creatomate + ~$0.10 Krea images + minimal ElevenLabs = under $0.50/video