Product Launch Motion — навык для Claude Code, который снимает промо-ролики продукта кодом
📂 Исходный код на GitHubНавык для Claude Code, который делает промо-ролики уровня Dribbble из кода: один элемент без монтажных склеек, пружинная анимация, курсор, который управляет всеми изменениями, звуки под биты и 60 fps. Вторая версия рассчитана на Claude Opus 5.5.
Product Launch Motion — это навык для Claude Code, который делает промо-ролик продукта из кода. Вы ставите навык, показываете Claude Code свой продукт и говорите: «Сними ролик запуска для моего продукта example.com. Ты режиссёр». Дальше навык сам придумывает сцены, собирает их в один HTML-файл и рендерит ролик в MP4.
Главная идея простая и необычная: ролик не монтируют из готовых кусочков. Каждый кадр считается из времени. Один и тот же элемент меняет форму, размер и цвет и перетекает из сцены в сцену — без единой монтажной склейки. Музыка идёт первой: сетку битов измеряют заранее, а каждый звук интерфейса синтезируют и ставят на его собственную измеренную вершину удара. На выходе — 60 fps с настоящим размытием движения.
Демо-ролик длиной 27 секунд сделан для cheerful.ai. Ни After Effects, ни готовых шаблонов, ни нейросетей для видео. Его можно скачать в 1080p и 60 fps со звуком. Вторая версия ролика сделана с WebGL-стеклом: прозрачные объёмные фигуры плывут сквозь все сцены.
Установка и первый запрос
npx skills add AbubakrChan/product-launch-motion
После установки достаточно одного запроса:
Сделай ролик запуска для моего продукта на example.com. Ты режиссёр.
Вторая версия навыка перебрана на более простом стеке и рассчитана на Claude Opus 5.5. Модели поменьше правила соблюдают, но упускают детали.
Семь правил, из-за которых ролик выглядит дорого
Каждое правило выросло из замечаний при проверке настоящего ролика. Все промпты и заметки лежат дословно в references/prompts.md.
- Один элемент, без склеек. Каждое состояние — это тот же самый элемент, который меняет размер, скругление и цвет. Переходы бывают трёх видов: превращение формы, движение камеры или наезд камеры в элемент, который становится следующей сценой.
- Смена содержимого через короткое размытие. Текст внутри меняющегося контейнера уходит рано и быстро, новый текст влетает на пружине. Старый и новый текст никогда не накладываются.
- Пружины с самым маленьким перелётом. Затухающие пружины в замкнутой форме, без резиновых смягчений. Кнопка переключателя и указатель вкладок растягиваются потому, что их края идут по разным пружинам.
- Курсор ведёт все изменения. Настоящие клики с нажатием и кругами расходятся от точки клика, настоящие перетаскивания возвращаются пружиной при отпускании. Агент самого продукта может стать вторым курсором.
- Камера обрамляет каждое состояние. Пустой кадр — это ошибка. Отъезд камеры показывает масштаб: одно письмо превращается в стену из сотен.
- Музыка идёт первой. Сетку битов измеряют с помощью numpy, дроп приходится на главный момент, а каждый синтезированный звук интерфейса ставится по своей измеренной вершине и попадает в бит.
- Настоящие данные и свои сцены. На цифровых барабанах крутятся реальные числа, логотипы клиентов бьются по одному на бит, а ничего из сайта продукта не копируется.
Восьмое правило — вы получаете режиссёра. Навык сам принимает творческие решения и записывает каждое отдельной строкой DECISION:. Одно ответное сообщение — и вы отменяете любое из них.
Что лежит в репозитории
| Путь | Что внутри |
|---|---|
| SKILL.md | Творческая установка и порядок работы, который соблюдает Claude |
| references/prompts.md | Исходные промпты и все замечания дословно |
| references/technique.md | Код пружин, камеры, курсора, барабана с цифрами, звука и рендера |
| legacy/ | Первая версия навыка: ролики под дикторский текст на GSAP и HyperFrames, 34 ловушки |
Требования
| Что нужно | Зачем |
|---|---|
| Claude Code с Opus 5.5 | Режиссура и сборка ролика |
| Node 20 или новее и Playwright | Рендер кадров |
| FFmpeg | Размытие движения, кодирование, проверка громкости |
| Python 3 и numpy | Сетка битов и синтез звуков |
| Свободная песня примерно 120 BPM | Необязательно. Лицензию на каждый трек нужно проверять: в некоторых бесплатных библиотеках есть ограниченные треки |
Всё считается локально и ничего не стоит денег.
Как устроен рендер
Каждый кадр — чистая функция времени. Схема работы такая:
бриф → список состояний по сетке битов → один HTML-ролик, каждый пиксель = f(t) в seek(t)
→ по кадру на бит, контактный лист, правки
→ Playwright: 4 подкадра на кадр
→ ffmpeg tmix (размытие движения) → 60 fps H.264 + звук → раскадровка и поиск швов
Из этого следует главное свойство: рендер повторяемый. Если перемотать на любой момент, получатся те же самые пиксели. Именно это делает возможным проверку по битам и параллельный рендер.
Пружины в замкнутой форме
Вместо CSS-переходов и таймеров используется аналитическая формула отклика затухающей ступени. Она считает значение в любой момент без накопления состояния между кадрами:
function stepR(tau, {w, z}) { // closed-form damped step response, 0 -> 1
if (tau <= 0) return 0;
if (z >= 1) { const x = w * tau; return 1 - Math.exp(-x) * (1 + x); }
const wd = w * Math.sqrt(1 - z * z);
return 1 - Math.exp(-z * w * tau) * (Math.cos(wd * tau) + (z * w / wd) * Math.sin(wd * tau));
}
class Track { // many target changes = sum of one spring per change
constructor(v0) { this.v0 = v0; this.cur = v0; this.ev = []; }
to(t, v, sp) { if (v !== this.cur) { this.ev.push([t, v - this.cur, sp]); this.cur = v; } return this; }
at(t) { let v = this.v0; for (const [ti, d, sp] of this.ev) v += d * stepR(t - ti, sp); return v; }
}
Затухание от 0.72 до 0.9 даёт самый маленький перелёт. Для камеры и текста берут единицу, то есть без перелёта. Цель, которая должна прийти к моменту T, задают примерно на 0.3 секунды раньше. Для зацикленных роликов складывают ещё периоды k = -1 и k = -2, чтобы последний кадр совпадал с первым.
Камера и слои
Слои такие: #bg с градиентом бренда, #thr с холстом для следа курсора, #world со сценой, которую двигает камера, и #hud с накладками в координатах экрана. Камера — это translate и scale на #world. Масштаб считается в логарифме: линейный зум ощущается неправильно.
Растяжение краёв
У переключателя и указателя вкладок ведущий край идёт по жёсткой пружине, а отстающий по мягкой. Ширина — это разность правого и левого краёв:
T.tgL = new Track(4).to(TOG, 44, {w: 19, z: 0.9}); // trailing
T.tgR = new Track(44).to(TOG, 84, {w: 34, z: 0.9}); // leading
Барабан с цифрами
У каждой цифры своя полоса 0–9, 0, а ячейка подогнана под ширину последнего знака. Обрезка идёт только по вертикали, через clip-path. Формула для цифры с номером p двигает её только тогда, когда нижняя цифра уже переносит единицу:
// digit p: floor(v / 10^p) % 10 + max(0, (v % 10^p) - (10^p - 1))
Звук под биты
numpy находит темп через спектральный поток и автокорреляцию, а удары — по диапазону низких частот. Дальше окно песни подбирается так, чтобы дроп попал на главный момент ролика. Отметки времени берутся из самого ролика, поэтому источник истины один:
cue(g) = g * PERIOD / 0.5 + PEAK_OFF // переводит время ролика в измеренное время удара
Сами звуки синтезируются: клик, щелчок, клавиша, нота, звонок, удар, свист, шум в полосе, блип, мерцание, подъём, плёнка. Звук перетаскивания следует за измеренной скоростью элемента. Сведение простое: песня 0.5 и звуки 0.62, затем одно постоянное усиление до пика 0.93. Ограничитель не нужен — так цикл остаётся бесшовным.
Рендер кадров
Playwright в headless-режиме снимает 1920×1080, на каждый кадр приходится четыре подкадра, и ffmpeg смешивает их в размытие движения:
ffmpeg -framerate 240 -i s%05d.png \
-vf "tmix=frames=4:weights='1 1 1 1',select='eq(mod(n\,4)\,3)',setpts=N/(60*TB),scale=out_color_matrix=bt709:out_range=tv,format=yuv420p" \
-r 60 -c:v libx264 -preset slow -crf 15 -tune film
Рендер идёт порциями: 960 подкадров в сегмент, затем PNG удаляются, сегменты склеиваются без перекодирования, сверху добавляется звук AAC. Три рабочих процесса. Скрипт терпит сбои: подкадр повторяется с чистой страницей, браузер перезапускается при разрыве связи.
Один элемент вместо одиннадцати сцен
Второй пример из репозитория показывает саму идею в чистом виде. Один элемент переходит через одиннадцать состояний интерфейса: кнопка, загрузка, галочка, динамическая полоса, видеолента, ползунок, переключатель, вкладки, график, палитра команд и всплывающее уведомление — и снова кнопка. Содержимое меняется через короткое размытие, курсор выполняет все клики и перетаскивания, последний кадр совпадает с первым. Ролик длиной 14 секунд идёт бесшовным циклом в формате 1440×1440, тоже со звуком: скачать.
Что запрещено
Список запрещённого пришёл из исходного задания и остаётся в силе:
- резиновые смягчения вместо пружин,
- вспышки частиц, свечения и градиенты на элементах интерфейса,
- разная толщина линий у иконок,
- пустые кадры и паузы,
- вид готового шаблона.
Ещё запрещено брать текст, интерфейс, статистику и картинки с сайта продукта целиком. Сцены придумываются своими.
Ловушки, которые стоят часов
Их собрал автор SKILL.md, и они сэкономят много времени:
will-changeна элементе, который масштабирует камера, делает текст размытым,- пружину запускают в момент события: цель, которая должна прийти к T, задают на 0.3 секунды раньше,
- функция
hide(), обнуляющая прозрачность, должна отменяться функцией отрисовки, иначе элементы остаются невидимыми, - в барабане с цифрами пропорциональные цифры прыгают: ячейку подгоняют под последний знак, обрезают только по вертикали,
- на сервере с тесным диском Chromium первым попадает под ограничение памяти: рендерят порциями по 4 секунды, браузер перезапускают, а посторонний
/tmp/inspect.pyперекрывает стандартную библиотеку, поэтому Python запускают из папки проекта.
Первая версия и вторая
| v1 (legacy) | v2 | |
|---|---|---|
| Тайминг | По словам дикторского текста | По измеренной сетке битов музыки |
| Движение | Таймлайны GSAP | Пружины в замкнутой форме, чистый seek(t) |
| Рендер | HyperFrames | Подкадры Playwright и tmix в ffmpeg |
| Структура | Сцены и переходы | Один элемент без склеек |
| Звук | Готовые звуки из библиотеки | Синтезированные звуки по измеренной вершине |
| Глубина | Камера на CSS | Пружины камеры и необязательный WebGL-стекло |
Для ролика с дикторским текстом первая версия всё ещё лучше. Она живёт в папке legacy.
Лицензия
MIT для навыка и материалов — LICENSE.
Демо-ролики не распространяются: они сделаны для Cheerful и принадлежат ему. Про это и про сторонние материалы — музыку, видео, шрифты — написано в NOTICE.