From content-vault
Builds a 30-60s motion-graphics launch video using Remotion 4, ElevenLabs narration and sound effects, with beat-anchored timing and brand-aligned design.
How this skill is triggered — by the user, by Claude, or both
Slash command
/content-vault:launch-videoThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Builds a **30-60s motion-graphics launch video** for a <YOUR_BRAND>
Builds a 30-60s motion-graphics launch video for a <YOUR_BRAND> product or feature, end-to-end:
script.timings.json. Edit copy → regen audio
→ all visuals auto-shift. No manual frame nudging.If your repo has a reference Remotion project, point this skill at it.
Otherwise the patterns described below stand alone. Reference
implementation path in the source repo: <YOUR_REFERENCE_VIDEO_PATH>.
Trigger on:
/launch-videoSkip for:
/graphics-designer/video-use
instead. That skill cuts on word boundaries from a Scribe transcript,
removes filler words, color grades, and burns subtitles. This skill is
for synthetic motion graphics built from scratch in Remotion. The
two compose: a /video-use edit can spawn Remotion overlay slots for
branded animation inserts, but the cut comes from /video-use.motion/<video-name>/
├── package.json # remotion 4 + react 19 + tsx + dotenv + mp3-duration
├── remotion.config.ts # publicDir → ../../brand/motion (single asset source)
├── tsconfig.json
├── SCRIPT.md # human-readable narration script (per beat)
├── .env # ELEVENLABS_API_KEY (gitignored!)
├── .env.example
├── generate-audio.ts # ElevenLabs narration runner, hash-cached per beat
├── generate-sfx.ts # ElevenLabs sound-generation runner, hash-cached
├── generate-music.ts # ElevenLabs music runner + leading-silence trim
├── script.timings.json # narration durations (generated)
├── sfx.timings.json # SFX durations (generated)
├── music.timings.json # music duration (generated)
└── src/
├── Root.tsx # registerRoot
├── Video.tsx # composition registry + LaunchVideoComposition wrapper
├── theme.ts # fps + duration + colors (mirrors <YOUR_BRAND_TOKENS_PATH>)
├── script.ts # narration beats (id, phase, text)
├── timings.ts # loader for script.timings.json + beatStartFrame()
├── sfx.ts # SFX clip definitions (anchored to frames)
├── music.ts # music prompt + volume
├── Narration.tsx # narration-only Sequence track
├── SoundDesign.tsx # narration + SFX + music combined
└── scenes/
├── HeroBackground.tsx # looped /video/hero-bg.mp4
├── OpeningScene.tsx # the orchestration scene (anchors all phases)
├── GlassCursor.tsx # multiplayer-style "you" cursor
├── CompanyTable.tsx # populating data table with column reveals
├── AgentDashboard.tsx # Google-Calendar week view
├── AgentRunDetail.tsx # tool-call trace view
├── MemoryPanel.tsx # stats + sparkline dashboard
├── Confetti.tsx # particle burst
└── ClosingCard.tsx # closing chat callback (the "Inviting" pattern)
Assets live in brand/motion/:
video/hero-bg.mp4 + poster (your brand's hero background loop · e.g.
an ambient video at brand/motion/video/hero-bg.mp4)logos/{models,tools,companies,sequencers}/ — third-party brand marksaudio/{launch-narration,sfx,music}/ — generated mp3s (committed for
reproducible renders)components/HeroChatMockup.tsx — framework-agnostic chat shellGet from the user:
Each beat = one narration sentence that lands during one visual phase. Typically 6-8 beats. Sum of beat audio lengths = total runtime. Beat lengths target 3-7s each.
For each beat capture: id, phase (one-line visual description), text
(spoken line, 10-20 words).
Save the script as a sibling of package.json for human review.
Format:
## Beat N — Phase name
**Frames** ~start–end (seconds) · **target ~Nwords**
> "Spoken line here."
**Visual sync:** what happens on screen.
src/script.tsMirrors SCRIPT.md as data. The narration generator and the composition
both import SCRIPT. No Remotion imports here so generators can run
in plain Node:
export interface Beat { id: string; phase: string; text: string; }
export const SCRIPT: Beat[] = [...];
export const AUDIO_SUBDIR = "audio/launch-narration";
export function audioPath(id: string) { return `${AUDIO_SUBDIR}/${id}.mp3`; }
cd motion/<video-name>
cp .env.example .env # paste ELEVENLABS_API_KEY
npm install
npm run generate-audio
This creates brand/motion/audio/launch-narration/{beat-id}.mp3 and
writes durations to script.timings.json. Hashes by beat text so
unchanged beats are skipped on subsequent runs (cheap iteration).
In OpeningScene.tsx:
const HERO_BEAT = beatStart("hero", 0); // → 0
const DATABASE_BEAT = beatStart("database", 0); // → 171 (after hero finishes)
// ... etc
BEAT_X + offset:
const BADGE_IN = HERO_BEAT + 6;
const TYPE_START = HERO_BEAT + 38;
const TOOLS_START = HERO_BEAT + 115; // synced to "GTM stack" lyric
const CURSOR_PRESS = HERO_BEAT + 165;
Define click/transition moments in src/sfx.ts. Anchor each clip to a
specific fromFrame using the same beatStartFrame() helper. Example
clips that earn their place:
npm run generate-sfx
Define one prompt + duration in src/music.ts. generate-music.ts
trims leading silence automatically (ElevenLabs music often opens with
a quiet bar that reads as "no music" against narration).
npm run generate-music
npm run render:landscape
Rendering takes ~30-90s. Always re-encode the output to yuv420p TV
range for QuickTime compatibility — the bg video is yuvj420p (JPEG
range) and the propagation breaks playback in some players:
ffmpeg -y -i out/launch-landscape.mp4 \
-c:v libx264 -profile:v high -level:v 4.0 \
-pix_fmt yuv420p -color_range tv -crf 18 -c:a copy \
out/launch-landscape-fixed.mp4
mv out/launch-landscape-fixed.mp4 out/launch-landscape.mp4
Use ffmpeg -ss N -i ... -frames:v 1 /tmp/check.jpg to spot-check
specific timestamps without watching the whole render.
leverage, synergy, robust, world-class,
seamless, intuitive, powerful, innovative.Light, neutral. One accent only, used once per scene where it earns meaning (live dot, highlight, success state).
| Token | Hex | Use |
|---|---|---|
--bg | #fafafa | page bg |
--surface | #ffffff | cards, dashboards |
--ink | #0a0a0a | primary text + numerals |
--muted | #71717a | secondary text |
--line | #e4e4e7 | borders |
--accent | #10b981 | the one accent |
'Helvetica Neue', Helvetica, Arial, sans-serifui-monospace, 'SFMono-Regular', Menlo-0.05em)0.16-0.18em,
color #71717a or #a1a1aa<span style={{ color: "#0a0a0a", fontWeight: 500 }}>bold</span> vs
surrounding #71717a text.Pick one per scene; don't mix:
backdrop-filter: blur(28px),
rgba(255,255,255,0.45).bg #ffffff, 1px solid #e4e4e7, subtle drop shadow.The bg-fade transition between modes is one of the most cinematic moments;
budget ~20-30 frames for it and use Easing.bezier(0.4, 0, 0.2, 1)
(material standard) for smoothness.
with_timestamps from ElevenLabs for word-level sync,
or estimate from beat duration / word count if approximate is fine.minHeight on the row prevents layout from jumping as content fills.
Looks janky if rows grow during the cascade.opacity gate animated via interpolate + ease curves.generate-audio.ts defaults to Bill
(pqHfZKP75CvOlQylNhV4) on eleven_multilingual_v2. Bill is the
advertisement use-case voice in ElevenLabs' library: wise, mature,
crisp. Reads as a real ad voiceover, not "AI guy on social media".
multilingual_v2 is the most stable + lifelike model for production
narration. Do not use eleven_turbo_v2_5 for launch videos —
it's the fast/cheap model and the difference is audible against a
hero bg video. Turbo is fine for previews / SFX timing checks, not
the final render.stability 0.40,
similarity_boost 0.75, style 0.30, use_speaker_boost true.
Per ElevenLabs best-practices: stability under 0.30 is unstable,
over 0.60 is monotone; similarity 0.75 is the docs sweet spot;
style 0.20–0.40 adds expressiveness without drift. Don't push style
above 0.4 unless you want noticeable inflection swings..env (VOICE_ID=... MODEL_ID=...)
or generate-audio.ts and re-run npm run generate-audio.cjVigY5qzO86Huf0OWal — smooth, trustworthy, modern SaaS feelHIGUfNOdjuWQwwapnTRW — pro clone, deep raspy, cinematicJBFqnCBsd6RMkjVDRZzb — British storyteller, narrative_storyjD4PjnscE4XmlzgsuqY0 — pro clone, young storytellernPczCjzI2devNBz1zQrb — the legacy default; labeled social_media, reads more "AI" than Billvoices_read permission. Some keys
don't have it; if you can't GET /v1/voices, fall back to known IDs.silenceremove ffmpeg pass strips it so playback hits at
frame 0. Keep that filter in place.0.10 reads as silent under narration; 0.15-0.18
is the sweet spot — clearly there but doesn't fight the voice.<Audio> without <Sequence>: it'll play continuously
from frame 0. Use <Series.Sequence> or <Sequence from={...}> to
place audio at specific positions.BEAT_X + offset. Re-derives automatically when
you regen audio.script.ts should not import
from remotion. Import staticFile lazily where needed.ffmpeg -ss N to verify..env (gitignored).script.ts as the single source of truth and
reference it from visuals where appropriate.The scene library is written to be reusable. For a new launch video, copy what fits and edit data + copy:
| Component | Use for |
|---|---|
HeroBackground | The looped hero background video |
GlassCursor | Multiplayer-style "you" cursor with label pill |
CompanyTable | Any populating tabular content with cascading columns |
AgentDashboard | Google-Calendar week-view with event cards |
AgentRunDetail | Step-by-step tool-call trace with reasoning headers |
MemoryPanel | Stats + dashboard preview pattern |
Confetti | Celebration burst (use sparingly, once per video max) |
ClosingCard | The "Inviting" chat callback closing pattern |
HeroChatMockup (in brand/motion) | Standalone chat shell for stills/static use |
For new components, follow the same pattern:
cardInFrame, firstRowFrame, rowStagger)staticFile)spring, interpolate, or interpolate with
Easing.bezier for smooth curves# 1. Scaffold the project (or copy a reference Remotion project)
cp -R motion/launch-video motion/<new-video-name>
cd motion/<new-video-name>
# 2. Reset state
rm -rf node_modules out
rm script.timings.json sfx.timings.json music.timings.json
echo "{}" > script.timings.json
echo "{}" > sfx.timings.json
echo "{}" > music.timings.json
rm -rf ../../brand/motion/audio/launch-narration/* \
../../brand/motion/audio/sfx/* \
../../brand/motion/audio/music/*
# 3. Install + add API key
npm install
cp .env.example .env # paste ELEVENLABS_API_KEY
# 4. Edit script.ts with the new beats
# 5. Edit OpeningScene.tsx to wire the new beats to scenes
# 6. Edit sfx.ts + music.ts prompts
# 7. Generate audio
npm run generate-all
# 8. Render + spot-check
npm run render:landscape
# repeat: tweak → render → check frames with ffmpeg
out/;
publishing to YouTube / LinkedIn / etc. is on the user.Once the MP4 renders cleanly:
mcp__claude_ai_Google_Drive__create_file
into your videos folder (<YOUR_DRIVE_VIDEOS_FOLDER>). Title pattern
<feature> · launch · YYYY-MM-DD.mp4.<YOUR_NOTION_CONTENT_DB_ID>). Create the row with Title,
Format=Video, Channel (LinkedIn / YouTube / X per where it's going),
Pillar=Promotional, Status=Scripting (if awaiting review) or
Scheduled, and paste the Drive viewUrl into Drive Assets.<YOUR_REFERENCE_VIDEO_PATH><YOUR_BRAND_DOC><YOUR_BRAND_TOKENS_PATH>npx claudepluginhub timscheuerai/content-vaultCreates product launch or SaaS promo videos from a URL, brief, or script. Routes to specialized sub-skills for explainers, site tours, PR videos, or captions when input doesn't match a product launch.
Generates a cinematic MP4 product launch video from a description using HTML/CSS animations, Playwright, and FFmpeg. No AI video APIs or runtime costs.
Plans and produces demo videos, GIFs, screenshots, and Remotion programmatic videos for product launches. Covers scripting, recording, best practices, and platform formats.