video-by-code · git:20261021.7501e1d · 2026-10-21 · sha256 65891021dd0a0a02
video-by-code git:20261021.7501e1dA
Immutable. This exact content never changes and is served at /api/v1/blob/65891021dd0a0a02.
---
name: video-by-code
description: >
Make a short video for Reels, Shorts or Telegram with no editing app and no paid service:
the agent writes the script, draws every frame in code on a browser canvas (SaaS-style
explainers, interface mock-ups, typed-out commands, diagrams) or cuts a dramatic piece out of
existing footage with ffmpeg filters and text overlays, synthesises the music and sound in
code, and renders an MP4 with three check-ins on the way. Use when asked for an explainer,
promo, motion video, a cut-down from a video, or «сделай ролик», «ролик кодом», «видео в стиле
SaaS», «нарезка из видео», «мультик-объяснялка». Not for illustrated or hand-drawn styles (cut
paper, ink, pixel art) — that is the `animate` skill — and not for editing a talking-head take
(that is `reels-console`).
---
# Ролик кодом
Два способа. Оба бесплатные, работают на своём компьютере, дают повторяемый результат:
тот же код — тот же ролик, любую сцену можно поправить отдельно.
| Способ | Когда | Как устроен | Заготовка |
|---|---|---|---|
| **Код-анимация** | объяснялка, промо, «SaaS-стиль», схемы, интерфейсы, текст, который печатается | кадр рисуется кодом на `<canvas>` в браузере; браузер без окна снимает кадр за кадром; ffmpeg собирает видео | `templates/canvas/` |
| **Нарезка** | драматичный ролик по готовому видео: кадры героя, тезисы поверх, атмосфера | ffmpeg режет отрезки исходника, красит их фильтрами, пишет текст, склеивает | `templates/cutdown/` |
Голоса в обоих нет: звук — синтезированная музыка и эффекты. Озвучка — необязательный шаг 7.
**Работай в копии, а не в папке скилла.** Заготовки лежат рядом с этим файлом. Под каждый
ролик скопируй нужную в рабочую папку проекта и правь там:
```bash
cp -r "<папка этого скилла>/templates/canvas" ./my-video # Windows PowerShell: Copy-Item -Recurse
```
Иначе следующий ролик начнётся не с чистой заготовки, а с правок прошлого.
## Установка (проверь, поставь недостающее)
```bash
node --version # нужен 18+
ffmpeg -version # любой свежий; ffprobe ставится вместе с ним
python --version # 3.9+, только для «нарезки»
```
Нет чего-то:
- **Windows:** `winget install OpenJS.NodeJS.LTS`, `winget install Gyan.FFmpeg`,
`winget install Python.Python.3.12`. После установки открыть терминал заново (PATH).
- **macOS:** `brew install node ffmpeg python`.
- **Linux:** `sudo apt install nodejs npm ffmpeg python3` (Node из apt бывает старым — тогда nvm).
Для «код-анимации» один раз в рабочей копии заготовки:
```bash
npm init -y && npm i playwright && npx playwright install chromium
```
Проверка, что всё живо: в рабочей копии выполнить `node render.mjs --sheet` — появится `sheet.png`
с пятью кадрами примера. Шрифты грузятся из Google Fonts, нужен интернет. Без него будет
системный шрифт и предупреждение в консоли.
## Порядок работы
Не делай всё одним махом: у человека три точки проверки, после каждой — его «ок».
1. **Бриф.** Что за ролик, для кого (канал, студенты, себе), формат (вертикаль 1080×1920 для
Reels/Shorts или 1920×1080 для YouTube), длина (обычно 30–75 с), настроение.
Есть видео-источник — достань суть: расшифровку (субтитры YouTube, `yt-dlp --write-auto-subs`,
или любой сервис транскриптов) и тезисы. Не придумывай фактов, которых нет в источнике.
2. **Сценарий → точка проверки 1.** Таблица сцен: длительность, что на экране, текст.
Правило: одна мысль на сцену, 4–8 секунд, на экране не больше 10–12 слов — их успевают
прочитать. Первая сцена — крючок, последняя — итог или призыв.
3. **Стиль → точка проверки 2.** Палитра (фон, текст, 1–2 акцента), шрифт, тип движения.
Собери 1–2 сцены и покажи лист кадров (`--sheet`). Не рендери весь ролик, пока стиль не
одобрен: правка стиля после рендера = рендер заново.
4. **Все сцены → лист кадров → точка проверки 3.** Проверь лист сам до показа (чек-лист ниже).
5. **Рендер** целиком (`node render.mjs` или `python cut.py plan.json`).
6. **Проверка готового ролика** (команды ниже): лист кадров по секундам, чёрные кадры, громкость.
7. *(Если нужен голос)* озвучка — см. раздел «Голос».
8. **Отдача:** MP4 (H.264, yuv420p, AAC). В Telegram видео до 50 МБ уходит как видео, не файлом.
## Код-анимация: как писать сцены
Открой `scenes.js` в рабочей копии. Ролик — массив `SCENES`, сцена = `{dur, draw(t)}`, где `t` — секунды
от начала сцены. Всё остальное — готовые рисовалки:
- `appear(t, start, dur)` → 0..1, «насколько появился» элемент. Умножай на прозрачность и сдвиг:
`text(..., alpha = appear(t, 0.3))`, `y = 600 + 40 * (1 - appear(t, 0.3))`.
- `text`, `wrap` (перенос по ширине), `card` (скруглённая панель), `gradient`, `typing`
(строка печатается сама), `progress` (полоска «шаг N из M»), `background` (живой фон).
- Плавность — `easeOut` для появления, `easeInOut` для перемещений. Линейное движение
выглядит дёшево.
**Железные правила:**
- Кадр зависит **только от `t`**. Нельзя `Date.now()`, `Math.random()`, `setTimeout`,
CSS-анимации, видео-теги — в покадровом рендере они дадут не то, что в окне.
Случайность — `seeded(…)`.
- Новый шрифт или вес — допиши в `FONTS`, иначе на холсте будет системный.
- Текст не ближе `SAFE` (90 px) к краям; в вертикали верх ~200 px и низ ~300 px закрыты
интерфейсом соцсети — ключевое туда не ставь.
- Первый кадр — обложка в ленте: на нём уже должен быть заголовок, не чёрный экран.
- Живой просмотр: открой `index.html` в браузере — ролик крутится в окне.
Рендер: `node render.mjs --sheet` (превью), `node render.mjs` (ролик в `out.mp4`, ~2 с
работы на секунду ролика). Размер и FPS — в начале `scenes.js` (`W`, `H`, `FPS`).
## Музыка кодом (`audio.js`)
Синтезатор браузера (Web Audio, `OfflineAudioContext`) играет партитуру в файл, а не в
динамики: аккорды — пилообразные волны через фильтр, бочка — синус с падающей частотой,
хэт и «шелест» на сменах сцен — шум через фильтр. Меняй `BPM`, `CHORDS` (MIDI-ноты),
громкости `VOL`. Шелест сам встаёт на стыки сцен. Чтобы смены кадров совпадали с тактом,
делай длительности сцен кратными доле (`60 / BPM`).
Своя готовая музыка вместо синтеза: в `render.mjs` замени `_audio.wav` на свой файл
(с правами на использование). Громкость при сборке выравнивается до −14 LUFS — стандарт лент.
## Нарезка: как работает `templates/cutdown/cut.py`
1. Скопируй `templates/cutdown/` в рабочую папку, положи исходник рядом (`input.mp4`),
скопируй `plan.example.json` в `plan.json`.
2. Найди кадры: лист по всему исходнику, кадр раз в 15 с с отметкой времени —
`ffmpeg -i input.mp4 -vf "fps=1/15,scale=384:-1,drawtext=text='%{pts\:hms}':fontcolor=white:fontsize=24:box=1:boxcolor=black,tile=6x5" -frames:v 1 contact.jpg`.
На Windows ffmpeg может ругаться «Fontconfig error» — добавь в `drawtext` шрифт явно:
`fontfile='C\:/Windows/Fonts/arial.ttf':`. Смотри глазами, выписывай `at` нужных моментов. `x` — где по ширине центр вертикального
кадра (лицо слева — 0.3).
3. `python cut.py plan.json --sheet` → по кадру на сцену; потом `python cut.py plan.json`.
Стили в `STYLES`: `horror` (обесцвечивание, контраст, красный канал, виньетка, мерцание по
формуле, зерно, гул + сердцебиение) и `clean`. Свой стиль — новый словарь с фильтрами ffmpeg
(`eq`, `colorchannelmixer`, `curves`, `vignette`, `noise`, `hue`). Текст пишется через
`textfile`, поэтому кавычки и двоеточия в тезисах не ломают команду; под текстом плашка.
Звук здесь тоже формулами ffmpeg (`aevalsrc`): гул — два близких синуса (дают «биение»),
сердцебиение — синус 50 Гц, гаснущий экспонентой, раз в секунду; плюс коричневый шум.
## Голос (необязательно)
Нужен голос за кадром — любой синтез речи в файл (ElevenLabs, OpenAI TTS, бесплатно —
`pip install edge-tts`, `edge-tts --voice ru-RU-DmitryNeural --text "…" --write-media vo.mp3`).
Порядок: сначала озвучка, потом длительности сцен под неё (`ffprobe` даёт длину фраз),
потом рендер. Смешать с музыкой, приглушая её под голосом:
```bash
ffmpeg -i out.mp4 -i vo.mp3 -filter_complex "[1:a]asplit[v][k];[0:a][k]sidechaincompress=threshold=0.03:ratio=8[m];[m][v]amix=inputs=2:normalize=0,loudnorm=I=-14:TP=-1.5[a]" -map 0:v -map "[a]" -c:v copy -c:a aac out_vo.mp4
```
## Проверка перед отдачей (обязательно)
```bash
# лист: кадр в секунду — смотри глазами каждый
ffmpeg -i out.mp4 -vf "fps=1,scale=180:-1,tile=10x3:padding=4:color=white" -frames:v 1 check.png
# чёрные куски (должно быть пусто)
ffmpeg -i out.mp4 -vf "blackdetect=d=0.1:pix_th=0.05" -an -f null - 2>&1 | grep black_start
# громкость: около -14 LUFS
ffmpeg -i out.mp4 -af ebur128 -f null - 2>&1 | grep "I:" | tail -1
```
Чек-лист по листу: текст не обрезан и не залезает в поля; читается на фоне (контраст;
красное на красном — плашку); первый кадр — не пустой; нет кадров, где сцена ещё не
появилась; одна мысль на экране; опечатки. Звук на слух ты не проверишь — скажи человеку
честно: «уровень в норме, на слух не проверял».
## Частые грабли
- Шрифт «не тот» на рендере → не дописан в `FONTS` или нет интернета.
- Ролик не открывается в телефоне/Telegram → не `yuv420p` или не H.264 (заготовки ставят сами).
- Окно браузера показывает одно, рендер другое → в `draw` есть время/случайность не от `t`.
- `drawtext` на Windows падает на пути к шрифту → путь через `/`, двоеточие экранировать
(`C\:/Windows/Fonts/arialbd.ttf`) — `cut.py` делает это сам; свой шрифт — `FONT=путь`.
- Долгий рендер упирается в таймаут агента → запускай в фоне и проверяй файл.
## Этика и права
- Кадры чужого видео — для себя или с правом использования; в публикацию — с указанием источника.
- Про живых людей: обвинения подавай как версии и ссылки на источник («по словам…»,
«суд не подтвердил»), не как факт. Клевета в ролике остаётся клеветой.
- Чужой бренд, логотип и «пишите мне» из исходника в свой ролик не переносить.
## Откуда подход
Идея «ролик = функция от времени на canvas + партитура в OfflineAudioContext + покадровый
рендер браузером без окна» — та же, что в скилле [`animate`](https://github.com/cth9191/animate)
(cth9191, MIT). Заготовки здесь написаны заново и проще: один стиль-интерфейс вместо
библиотеки рисованных стилей, плюс нарезка из готового видео, которой там нет.