From Video Production
Records HTML animations as MP4/GIF/WebM using Playwright and FFmpeg. Supports frame-exact deterministic capture for CI and short social explainers.
How this skill is triggered — by the user, by Claude, or both
Slash command
/video-media:video-exportWhen to use
Артефакт через `animations` skill готов, нужно отправить как video file. После `animations` если результат должен быть посланным в TG/Twitter/YouTube Shorts.
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
HTML → series of frames → FFmpeg encode → MP4/GIF/WebM. Качество и контроль над framerate.
HTML → series of frames → FFmpeg encode → MP4/GIF/WebM. Качество и контроль над framerate.
brew install ffmpeg # mac
sudo apt-get install ffmpeg # linux
choco install ffmpeg # windows
npm i -D playwright
const { chromium } = require('playwright');
const browser = await chromium.launch();
const ctx = await browser.newContext({
viewport: { width: 1920, height: 1080 },
recordVideo: { dir: './videos/', size: { width: 1920, height: 1080 } },
});
const page = await ctx.newPage();
await page.goto('file:///abs/animation.html');
await page.waitForTimeout(10000); // record 10 sec
await ctx.close(); // обязательно — сохраняет видео
await browser.close();
// → videos/<random>.webm
WebM → конвертить в MP4 для широкой совместимости:
ffmpeg -i videos/animation.webm -c:v libx264 -preset slow -crf 18 \
-pix_fmt yuv420p -movflags +faststart out.mp4
Контроль над каждым кадром:
const fps = 60;
const duration = 5; // sec
const frames = fps * duration;
for (let i = 0; i < frames; i++) {
const t = i / fps; // time in seconds
// Установить state на момент времени (если animations завязаны на JS time)
await page.evaluate((t) => {
window.__animTime = t;
window.dispatchEvent(new Event('frame'));
}, t);
await page.screenshot({
path: `frames/frame-${String(i).padStart(5, '0')}.png`,
});
}
В animations.jsx использовать window.__animTime вместо Date.now():
function useTime() {
const [t, setT] = useState(0);
useEffect(() => {
const tick = () => setT(window.__animTime ?? performance.now() / 1000);
window.addEventListener('frame', tick);
return () => window.removeEventListener('frame', tick);
}, []);
return t;
}
Затем FFmpeg:
ffmpeg -framerate 60 -i frames/frame-%05d.png \
-c:v libx264 -preset slow -crf 18 \
-pix_fmt yuv420p -movflags +faststart out.mp4
Метод 2 даёт deterministic видео — кадры идентичны на каждом запуске. Хорошо для CI / regenerable.
GIF — большие файлы, ограниченная палитра. Для коротких циклов (~5 сек) ОК:
# Two-pass: palette generate → encode
ffmpeg -i frames/frame-%05d.png -vf "fps=30,scale=720:-1:flags=lanczos,palettegen" palette.png
ffmpeg -framerate 30 -i frames/frame-%05d.png -i palette.png \
-filter_complex "fps=30,scale=720:-1:flags=lanczos[x];[x][1:v]paletteuse" out.gif
GIF size mistakes:
| Платформа | Resolution | FPS | Codec | Длительность |
|---|---|---|---|---|
| YouTube Shorts | 1080×1920 | 30/60 | H.264 | < 60s |
| Instagram Reels / TikTok | 1080×1920 | 30 | H.264 | 15-90s |
| Twitter video | 1280×720 | 30 | H.264 | < 140s |
| LinkedIn video | 1920×1080 | 30 | H.264 | < 10min |
| Telegram | 1920×1080 max | 30 | H.264 | unlimited |
| Email GIF | 600×400 max | 15-30 | GIF | < 6MB |
-crf | Quality | Размер |
|---|---|---|
| 17 | Visually lossless | максимум |
| 18-22 | Good | high |
| 23 (default) | Standard | medium |
| 28-30 | Lower | small |
| > 35 | Bad | tiny |
Для финального deliverable: -crf 18 + -preset slow.
Для preview / debug: -crf 28 + -preset ultrafast.
Добавить музыку:
ffmpeg -i out.mp4 -i music.mp3 -c:v copy -c:a aac -b:a 192k -shortest out-with-audio.mp4
-shortest — обрежется по кратчайшему трекy. Без -shortest — видео loops пока музыка играет.
Чтобы GIF / видео плавно зацикливалось, последний кадр === первому. В animations.jsx:
function useLoopTime(duration) {
const t = useTime();
return (t % duration); // 0 → duration → 0 → ...
}
animations skill — каркас анимации (anim-engine.jsx)verifier — проверить что HTML открывается без errorsplaceholders — для статичных элементов внутри анимации-pix_fmt yuv420p → не работает на Apple devices-movflags +faststart → видео грузит всё перед началом проигрывания (web-плохо)Date.now() вместо deterministic time → каждый запуск разныйnpx claudepluginhub jhamidun/claude-code-config-pack --plugin video-mediaBuilds a throwaway prototype to answer a design question about UI appearance or state/logic behavior. Guides you through two branches: interactive terminal app for logic validation, or multiple UI variations for visual exploration.