Skip to content
Marketing
Skill

/vid-motion-graphics

Generates motion graphics videos (MP4) from a content brief. Multi-scene HTML/CSS animations rendered frame-by-frame in headless Chromium via Playwright, assembled with FFmpeg. 1080×1080 default, 16:9 (1920×1080) and 9:16 (1080×1920) supported. 5 style presets. Trigger when user

From plugin
opendirectory-gtm-skills
58364 skills
Install
$ npx -y skills add Varnan-Tech/opendirectory --skill vid-motion-graphics --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/vid-motion-graphics

Context preview

The summary Claude sees to decide when to auto-load this skill.

Generates motion graphics videos (MP4) from a content brief. Multi-scene HTML/CSS animations rendered frame-by-frame in headless Chromium via Playwright, assembled with FFmpeg. 1080×1080 default, 16:9 (1920×1080) and 9:16 (1080×1920) supported. 5 style presets. Trigger when user

SKILL.md

vid-motion-graphics.SKILL.md
name: vid-motion-graphics
description: Generates motion graphics videos (MP4) from a content brief. Multi-scene HTML/CSS animations rendered frame-by-frame in headless Chromium via Playwright, assembled with FFmpeg. 1080×1080 default, 16:9 (1920×1080) and 9:16 (1080×1920) supported. 5 style presets. Trigger when user says "create a video", "motion graphic", "animated video", "make a reel", "create an explainer", "animated infographic", or "short video".
compatibility: [claude-code, gemini-cli, github-copilot]
author: OpenDirectory
version: 1.0.0

vid-motion-graphics

Generates multi-scene motion graphics as MP4. Renders HTML/CSS animations in headless Chromium via Playwright (Web Animations API frame-seeking), assembles PNG frames with FFmpeg. No React, no AI APIs, no Python — zero new dependencies beyond the graphic-gif family.

CDN fonts only. No external libraries in HTML.

---

Critical Rules (read before every generation)

1. **Use `window.renderFrame(t)` — no CSS `@keyframes` for scene transitions.** CSS animation `currentTime` seeking is silently ignored for backward seeks in Chromium. The renderFrame approach: a pure JS function computes `opacity`/`transform` directly from milliseconds. Playwright calls it once per frame. Deterministic, race-free. 2. **No `animation-delay` on ANY element.** Not needed with renderFrame. If you catch yourself writing `animation-delay`, stop — you're using the wrong architecture. 3. **`window.__videoReady = true` only inside `document.fonts.ready.then(...)`.** Never set synchronously — fonts must load before Playwright captures frame 1 or text renders with fallback fonts. 4. **Expose `window.__stopPreview()`.** The browser's rAF preview loop races with Playwright's evaluate/screenshot calls. `capture-frames.mjs` calls `__stopPreview()` before the frame loop. Always include it. 5. **Use `t < startMs` (not `<=`) in scene boundary checks.** `t <= 0` at frame 0 makes scene 1 black. The correct guard is `if (t < startMs || t >= endMs) return hidden`. 6. **Body = exact pixel dimensions.** Width and height are integers (`1080px`, `1920px`). No `%`, `vw/vh`, or responsive units. 7. **No two scenes visible simultaneously** (except 10% enter overlap). All scenes `opacity: 0` outside their renderFrame window. 8. **Transitions use `opacity` only.** No `display` toggle, no `visibility` — GPU-composited opacity is frame-perfect. 9. **Never dump HTML in chat.** Save to file, show summary only. 10. **Title states the message, not the topic.** "3 Reasons Q4 Crushed Targets" not "Q4 2024 Performance Video". 11. **Read `references/scene-library.md` before generating ANY HTML.** Use exact HTML structure and CSS class names from that file.

---

Step 1: Intake

**Required:** `content_brief`

**Optional parameters and defaults:**

| Parameter | Default | Description | |---|---|---| | content_brief | — | Text describing what the video communicates (required) | | scenes | auto | Number of scenes (1–6). Auto = derived from brief. | | duration_per_scene | 3s | Duration per scene in seconds (1–8s) | | style | kinetic-dark | kinetic-dark / editorial-light / data-pulse / bold-type / minimal-clean | | aspect_ratio | 1:1 | 1:1 (1080×1080) / 16:9 (1920×1080) / 9:16 (1080×1920) | | fps | 30 | Frames per second (24, 30, or 60) | | music | none | Path to audio file for background track (mp3/m4a/wav) | | source | — | Source attribution shown in final frame footer |

**If `content_brief` is missing, ask exactly:**

> "To create the video, I need a content brief — what should the video communicate? > > Example: 'Show 3 reasons why Q4 revenue grew 85%: new enterprise deals, reduced churn, price increase. Use bold numbers. Style: data-pulse.' > > Optional: style (default: kinetic-dark), aspect ratio (default: 1:1), seconds per scene (default: 3s)"

If `content_brief` is present → proceed to Step 2 immediately.

---

Step 2: Internal Architecture (never shown to user)

**1. Parse brief into scenes (max 6):**

  • Scene 1 = hook or title (always)
  • Scenes 2–N-1 = supporting points, metrics, or story beats
  • Scene N = CTA or closing summary (always, if more than 1 scene)
  • One key idea per scene — if brief has 7+ ideas, consolidate the weakest ones

**2. Read `references/scene-library.md`** — choose scene type for each scene:

  • Hook/opening → `title-card`
  • Single metric → `stat-reveal`
  • List of 2–4 points → `bullet-list`
  • Before vs after / two values → `split-screen`
  • Testimonial / quote → `quote-card`
  • Final / CTA → `cta-card`

**3. Read `references/style-presets.md`** — load CSS tokens + animation personality for chosen style.

**4. Calculate timing:**

totalDuration = sceneCount × duration_per_scene  (seconds)
totalFrames   = totalDuration × fps

Each scene occupies `(100 / sceneCount)%` of the total `@keyframes` range.

| Scene | Start % | End % | |---|---|---| | 1 | 0% | (100/N)% | | 2 | (100/N)% | (200/N)% | | … | … | … | | N | ((N-1)×100/N)% | 100% |

Within each scene's range:

  • Enter: first 10% of scene range
  • Hold: 10% to 85% of scene range
  • Exit: 85% to 100% of scene range

**5. Determine pixel dimensions:**

  • `1:1` → W=1080, H=1080
  • `16:9` → W=1920, H=1080
  • `9:16` → W=1080, H=1920

---

Step 3: HTML Generation

Read `references/scene-library.md` AND `references/style-presets.md` before writing any code.

**Required HTML structure:**

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[font CDN link from style preset]
<style>
:root {
  [all CSS tokens from style preset]
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
  width: [W]px; height: [H]px;
  overflow: hidden;
  background: var(--bg);
  font-family: var(--font-body);
  position: relative;
}

.scene {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px;
  opacity: 0;
  will-change: opacity, transform;
}
.scene-inner {
  width: 100%;
  max-wi
Read more
Ships withopendirectory-gtm-skills

AI Agent Skills built for Founders who hate Marketing

Get the whole plugin