Install
openclaw skills install skill-5AI video production workflow using Remotion. Use when creating videos, short films, commercials, or motion graphics. Triggers on requests to make promotional...
openclaw skills install skill-5Create professional motion graphics videos programmatically with React and Remotion.
output/<project-name>/npm installnpx remotion (not bun):
"scripts": {
"dev": "npx remotion studio",
"build": "npx remotion bundle"
}
cd output/<project-name> && npm run dev
Wait for "Server ready" on port 3000.bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000
https://xxx.trycloudflare.com)The user will preview in their browser, request changes, and you edit the source files. Remotion hot-reloads automatically.
cd output/<project-name>
npx remotion render CompositionName out/video.mp4
# Scaffold project
cd output && npx --yes create-video@latest my-video --template blank
cd my-video && npm install
# Add motion libraries
npm install lucide-react
# Fix scripts in package.json (replace any "bun" references with "npx remotion")
# Start dev server
npm run dev
# Expose publicly
bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000
MANDATORY: When a video mentions or features any product/company, use Firecrawl to scrape the product's website for brand data, colors, screenshots, and copy BEFORE designing the video. This ensures visual accuracy and brand consistency.
API Key: Set FIRECRAWL_API_KEY in .env (see TOOLS.md).
bash scripts/firecrawl.sh "https://example.com"
Returns structured brand data: brandName, tagline, headline, description, features, logoUrl, faviconUrl, primaryColors, ctaText, socialLinks, plus screenshot URL and OG image URL.
mkdir -p public/images/brand
curl -s "https://example.com/favicon.svg" -o public/images/brand/logo.svg
curl -s "${OG_IMAGE_URL}" -o public/images/brand/og-image.png
curl -sL "${SCREENSHOT_URL}" -o public/images/brand/screenshot.png
Use scene-based architecture with proper transitions:
const SCENE_DURATIONS: Record<string, number> = {
intro: 3000, // 3s hook
problem: 4000, // 4s dramatic
solution: 3500, // 3.5s reveal
features: 5000, // 5s showcase
cta: 3000, // 3s close
};
import {
AbsoluteFill, Sequence, useCurrentFrame,
useVideoConfig, interpolate, spring,
Img, staticFile, Audio,
} from "remotion";
export const MyVideo = () => {
const frame = useCurrentFrame();
const { fps, durationInFrames } = useVideoConfig();
return (
<AbsoluteFill>
{/* Background music */}
<Audio src={staticFile("audio/bg-music.mp3")} volume={0.35} />
{/* Persistent background layer - OUTSIDE sequences */}
<AnimatedBackground frame={frame} />
{/* Scene sequences */}
<Sequence from={0} durationInFrames={90}>
<IntroScene />
</Sequence>
<Sequence from={90} durationInFrames={120}>
<FeatureScene />
</Sequence>
</AbsoluteFill>
);
};
slideLeft, slideRight, crossDissolve, fadeBlur presetsnpm install lucide-react) — never emoji// Timing values (in seconds)
const timing = {
micro: 0.1-0.2, // Small shifts, subtle feedback
snappy: 0.2-0.4, // Element entrances, position changes
standard: 0.5-0.8, // Scene transitions, major reveals
dramatic: 1.0-1.5, // Hero moments, cinematic reveals
};
// Spring configs
const springs = {
snappy: { stiffness: 400, damping: 30 },
bouncy: { stiffness: 300, damping: 15 },
smooth: { stiffness: 120, damping: 25 },
};
const opacity = interpolate(frame, [0, 30], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp"
});
const scale = spring({
frame, fps,
from: 0.8, to: 1,
durationInFrames: 30,
config: { damping: 12 }
});
<Sequence from={0} durationInFrames={100}>
<Scene1 />
</Sequence>
<Sequence from={80} durationInFrames={100}>
<Scene2 />
</Sequence>
Place persistent elements OUTSIDE Sequence blocks:
const PersistentShape = ({ currentScene }: { currentScene: number }) => {
const positions = {
0: { x: 100, y: 100, scale: 1, opacity: 0.3 },
1: { x: 800, y: 200, scale: 2, opacity: 0.5 },
2: { x: 400, y: 600, scale: 0.5, opacity: 1 },
};
return (
<motion.div
animate={positions[currentScene]}
transition={{ duration: 0.8, ease: "easeInOut" }}
className="absolute w-32 h-32 rounded-full bg-gradient-to-r from-coral to-orange"
/>
);
};
Before delivering, verify:
npm run dev on port 3000bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000my-video/
├── src/
│ ├── Root.tsx # Composition definitions
│ ├── index.ts # Entry point
│ ├── index.css # Global styles
│ ├── MyVideo.tsx # Main video component
│ └── scenes/ # Scene components (optional)
├── public/
│ ├── images/
│ │ └── brand/ # Firecrawl-scraped assets
│ └── audio/ # Background music
├── remotion.config.ts
└── package.json
See references/components.md for reusable:
# Start tunnel (exposes port 3000 publicly)
bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000
# Check status
bash skills/cloudflare-tunnel/scripts/tunnel.sh status 3000
# List all tunnels
bash skills/cloudflare-tunnel/scripts/tunnel.sh list
# Stop tunnel
bash skills/cloudflare-tunnel/scripts/tunnel.sh stop 3000