| name | video-graphics |
| description | VideoGraphics — a Remotion skill for agentic AI video editors. Use when Brad or any user wants to add motion graphics overlays to their videos. Loads the VideoGraphics framework to generate, preview, review, iterate, and render Remotion compositions via a structured conversation. Works with any LLM agent (Hermes, Claude, GPT-Codex, Minimax, Kimi, GLM). Requires Remotion, Node.js, and ffmpeg on the user's machine. NOT for simple video cuts, audio mixing, or color grading.
|
VideoGraphics — Agentic Motion Graphics Framework
What it is
A Remotion skill for AI agents. The agent reads this skill, then has a structured
conversation with the user to generate motion graphics overlays — lower thirds,
title cards, spec badges, product reveals — render them as transparent WebMs,
and deliver them for compositing in Filmora (or equivalent).
Not a standalone tool. Requires an agent to run it. The agent handles all
Remotion code generation and render pipeline management — the user just reviews,
requests changes, and approves.
Core Workflow
USER BRIEF
↓
ASK FOR PROJECT FOLDER / ASSETS
↓
INFER OR ASK STYLE DIRECTION
↓
GENERATE COMPOSITION CODE
↓
STILL FRAME PREVIEW ← user reviews layout/colors/spacing
↓
USER REQUESTS CHANGES (or confirms)
↓
ITERATE → PREVIEW AGAIN
↓
USER APPROVES
↓
RENDER FINAL (alpha WebM or MP4)
↓
RECORD APPROVAL → UPDATE PREFERENCE PROFILE
Framework Location
~/.hermes/skills/video-graphics/
├── framework/
│ ├── setup.js # Create a new project
│ ├── render.js # Preview + render pipeline
│ ├── preference-engine.js # Learn from approvals
│ ├── style-inferencer.js # Infer style from natural language
│ └── composition-library.js # Built-in composition types
├── projects/ # One folder per project
└── global-assets/ # Reusable elements (subscribe, Q&A, etc.)
Step-by-Step Agent Guide
Step 1 — Identify or Create Project
Ask the user: "Which project is this for?"
If new project:
- Run
node framework/setup.js <project-name> <video-type>
- video-type:
product | related-topic | comparison
- Tell the user the project folder path
- Ask them to drop their assets into the project folder
If existing project:
- Run
node framework/preference-engine.js context <project-name>
- Include the output in your context — it contains learned preferences
- Ask if they want to use the learned style or start fresh
Step 2 — Collect Assets
Ask the user what assets they have in the project folder:
- Product images / thumbnails
- Logos / wordmarks
- Brand color codes (hex)
- Any reference images
Tell them: "Drop whatever you have into the project folder — I'll work with what's there."
If the user has a global asset concept (subscribe overlay, Q&A ask), point them to the global-assets folder.
Step 3 — Style Direction
Ask the user: "What style are you going for?"
Accept any natural-language description. Examples:
- "POCO yellow and black, fast and energetic, racing vibe"
- "minimal, clean, Apple-style white on dark"
- "premium dark blue with gold accents, slow and cinematic"
- "#1A1A2E with #E94560 accent, tech reviewer aesthetic"
Run the style inferencer:
node framework/style-inferencer.js expand "<user description>" <video-type>
This gives you structured parameters to pass to the composition generator.
Step 4 — Generate Composition
Choose the right composition type from the library:
| Video Type | Common Compositions |
|---|
| Product | TitleCard, LowerThird, SpecBadgeRow, CountUp, ProductCarousel, PriceCard |
| Related Topic | TitleCard, LowerThird, GradientOverlay, AccentBar |
| Comparison | ComparisonSplit, SpecBadgeRow, TitleCard, PriceCard |
Generate the Remotion React code. The agent writes the .tsx file in the project's compositions/ folder.
Critical rules for generated code:
- Always
Config.setVideoImageFormat("png") in remotion.config.ts before rendering
- Always use spring-based animations (react-spring)
- Always apply 175% font scale (mobile-first — non-negotiable)
- Use the style parameters from Step 3
- Use the animation grammar: gradient enters first, content after delay; content exits first, gradient exits last
Step 5 — Still Frame Preview
Before any video render, always generate a still frame:
node framework/render.js preview <CompositionId> --project=<slug> --frame=120
Upload/send the preview PNG to the user. Review checklist:
- Layout correct? (text position, element alignment)
- Colors right? (matches style direction?)
- Font scale readable? (too small for phone viewers?)
- Content accurate? (correct text, correct values?)
Step 6 — User Review
Ask: "What do you think? Any changes?"
Collect feedback. Common change types:
- Color wrong → update color variables, re-preview
- Font too small → increase fontScale by 0.25, re-preview
- Animation too fast/slow → adjust damping/stiffness, re-preview
- Layout off → adjust positioning, re-preview
- Wrong text/content → fix content, re-preview
If changes requested → iterate back to Step 4/5.
Step 7 — User Approves
When the user says it's good:
node framework/preference-engine.js approve <project> <CompositionId> <notes>
Notes can include what was approved — color choices, animation speed, font scale.
Then render the final:
node framework/render.js render <CompositionId> --project=<slug> --format=webm
node framework/render.js render <CompositionId> --project=<slug> --format=mp4
Verify alpha:
node framework/render.js verify <output-file.webm>
Output goes to projects/<slug>/output/.
Step 8 — Compositing Instruction
Tell the user how to use the output in Filmora:
- Put background video on Track V1 (lower track)
- Put the rendered WebM on Track V2 (upper track, directly above)
- Transparent areas reveal the background video
- Export the composite as final
Composition Types Reference
LowerThird
Transparent overlay, bottom 36% of frame. Slide-in gradient + staggered content. Exit: content leaves first, gradient follows.
- accepts: title, subtitle, accentColor, logo
TitleCard
Full-screen title screen. Per-character spring reveal. Transparent or opaque.
- accepts: title, subtitle, backgroundColor, font, fontScale
SpecBadgeRow
Horizontal row of spec badges. Spring stagger entry. Count-up for numeric values.
- accepts: specs: Array<{name, value}>, badgeColor
CountUp
Number counts from 0 to target. No floating-point jitter (uses interpolate trick).
- accepts: target (number), unit, prefix
ProductCarousel
Horizontal swipe of product images. Spring physics swipe.
- accepts: images: string[], labels: string[]
PriceCard
Price display with badge, strike-through original, highlight current.
- accepts: currentPrice, originalPrice, currency, badge
ComparisonSplit
Side-by-side A vs B comparison. Split slide-in, winner badge.
- accepts: productA, productB, winner: A|B
GradientOverlay
Colored gradient overlay. Fade in → hold → fade out.
- accepts: colors: string[], direction, opacity
AccentBar
Single accent bar with optional glow. Horizontal or vertical.
- accepts: color, direction, glow, position
SubscribeAsk
Fixed subscribe CTA. Lives in global-assets/.
- accepts: channelName, accentColor
Style Inferencer — Output Reference
node style-inferencer.js expand "<description>" <video-type> returns:
{
"videoType": "product",
"userRawInput": "poco yellow and black, fast and energetic",
"style": {
"primaryColor": "#000000",
"accentColor": "#FFD500",
"textColor": "#FFFFFF",
"allColors": ["#000000", "#FFD500", "#FFFFFF"],
"animation": { "damping": 12, "stiffness": 350, "mass": 0.8 },
"duration": 8,
"fontScale": 1.75,
"gradientDirection": "to top",
"accentBar": { "position": "left", "width": 4 },
"glow": false
},
"animationGuidelines": {
"damping": 12,
"stiffness": 350,
"mass": 0.8,
"entryStart": 0,
"contentEnter": 20,
"exitStart": 210,
"exitComplete": 240
}
}
Animation Grammar (Non-Negotiable)
For all lower-third / overlay compositions:
ENTRY: gradient enters first (spring, 0 → complete)
content enters after (spring, +20 frames delay)
EXIT: content exits first (spring, -30 frames from end)
gradient exits last (spring, ends at last frame)
Never: gradient and content appearing/leaving simultaneously.
Preference Profile — What Gets Learned
Every approval records:
- Colors used
- Animation speed (damping/stiffness)
- Font scale
- Duration
- User notes
- Timestamp
When starting a new composition for the same project, always call:
node framework/preference-engine.js context <project-name>
And apply those preferences to the new composition.
Global Assets
Reusable elements that live in skills/video-graphics/global-assets/:
| Asset | Use |
|---|
| SubscribeAsk | End-card subscribe CTA |
| QandAAsk | Mid/video Q&A prompt overlay |
| TimestampMarker | Chapter / timestamp cards |
| BrandLowerThird | Brand logo lockup lower third |
These are pre-built. The user drops them into their project or references them in Filmora directly.
System Requirements
The user's machine needs:
- Node.js (v18+)
- npm (comes with Node)
- ffmpeg (
ffmpeg -version to check)
- Remotion project — set
$REMOTION_ROOT env var pointing to your Remotion project
If any are missing, guide the user to install them before starting.
Style Inferencer — Keyword Matching Rule (Critical)
When mapping natural-language keywords to structured parameters, always use word-boundary regex to avoid false matches:
const hasAccentBar = text.includes("bar");
const accentBarRegex = /\b(accent|bar|stripe|line|divider)\b/i;
const hasAccentBar = accentBarRegex.test(text);
Keywords that produce only colors are simple arrays. Keywords that carry semantic meaning (brand, energy, animation style) should be extended palette objects:
const COLOR_INFERENCE = {
premium: ["#1A1A2E", "#16213E", "#0F3460"],
gaming: ["#7B2FFF", "#00E5FF", "#FF3D00", "#000000"],
poco: {
colors: ["#FFD500", "#000000", "#FFFFFF"],
accentBar: true,
animation: "fast",
},
racing: {
colors: ["#FFD500", "#000000", "#FF3D00"],
accentBar: true,
animation: "fast",
},
};
When processing keywords:
- Check if palette is an extended object → pull
colors, accentBar, animation flags
- Check if palette is a simple array → just pull colors
- Set animation from keyword first, then allow explicit word overrides to take priority
let keywordAccentBar = false;
let keywordAnimation = null;
for (const [keyword, palette] of Object.entries(COLOR_INFERENCE)) {
if (text.includes(keyword)) {
if (typeof palette === "object" && !Array.isArray(palette)) {
colors.push(...palette.colors);
if (palette.accentBar) keywordAccentBar = true;
if (palette.animation) keywordAnimation = palette.animation;
} else {
colors.push(...palette);
}
}
}
Then later in the function:
const accentBarRegex = /\b(accent|bar|stripe|line|divider)\b/i;
const hasAccentBar = keywordAccentBar || accentBarRegex.test(text);
Alpha Truth (Critical)
VP9 alpha files ALWAYS show pix_fmt=yuv420p in ffprobe. This is normal.
Real alpha indicator: TAG:alpha_mode=1
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,pix_fmt:stream_tags=alpha_mode \
-of default=noprint_wrappers=1:nokey=0 out/file.webm
Always verify after render. If standalone player shows black over transparent areas — this is normal. Trust Filmora timeline compositing + alpha_mode=1.
Key Rules Summary
- Still frame preview first — never render full video without user approval
- Font scale 175% — desktop preview is misleading for phone viewers
- Gradient grammar — gradient enters first, content exits first
- Alpha check — look for
TAG:alpha_mode=1, not pix_fmt
- Three flags —
--codec=vp9 --pixel-format=yuva420p --image-format=png
- Approval = learning — always record approvals to the preference profile
- User owns assets — don't download anything, just work with what they provide
- Duration minimum — 8 seconds (240 frames at 30fps) for readable overlays
- Transparency — compositions are transparent unless the user explicitly wants opaque
- Composition one-at-a-time — render, review, approve, next