Пятнадцать с половиной секунд. Бумажный бостон-терьер идёт по каменному мостику на закате, дважды прыгает в осеннем лесу, разгоняется до галопа в снежных горах, и в конце на экран садится логотип pimenov.ai. Выглядит как диорама из вырезанной бумаги, снятая на камеру.

Бумаги и камеры здесь нет. Нет и ни одного файла с картинкой. Весь ролик нарисовал кодом мой видеобот Reels Studio, который работает в Grok Bot. Внутри — страница на HTML и JavaScript, формы на Canvas2D, глубина и фактура на three.js и шейдерах, рендер в браузере без окна (headless Chrome). Бот записывал каждый сетевой запрос страницы при рендере. При рендере сцен демо страница делает девять запросов: сама HTML-страница и восемь модулей JavaScript. Запросов изображений, шрифтов и медиа — ноль. Единственный внешний файл появляется в концовке: официальный SVG-логотип pimenov.ai, и бот вставил его в страницу как текст разметки.

Кадр, описанный кодом, ведёт себя как программа. Его можно перерисовать заново, повторить байт в байт, поправить одну деталь и доказать, что остальное не сдвинулось ни на пиксель. Дальше — как бот к этому пришёл и что из этого можно повторить у себя.

Три терьера и один выбор

Начали с одного статичного кадра 1920×1080: терьер на каменном мостике на закате. Бот нарисовал его в трёх вариантах:

  • A — оригами. Плоские грани и складки, каждая грань затенена так, будто у фигуры есть объём.
  • B — мягкий мультяшный. Голова в 1,42 раза крупнее, огромные глаза с бликами, язычок, короткие лапки.
  • C — реалистичные пропорции. Квадратный корпус и короткая гладкая шерсть из сотен бумажных «чешуек», уложенных по направлению роста шерсти.

Порода узнаётся во всех трёх: чёрно-белый окрас, белая полоса по центру морды, белая грудь и «носочки», стоячие уши-«летучие мыши», большие круглые глаза, короткий хвост. Общий аксессуар — красный ошейник с золотой звёздочкой, которая рифмуется со звездой на небе.

Бот провёл три раунда самокритики. В первом раунде A читался как чихуахуа, поэтому уши расширили, а корпус сделали квадратным. У C шерсть получилась лохматой, её пригладили и обрезали по силуэту.

Три образа одного кадра: A — оригами, B — мультяшный, C — реалистичный. Сверху полные кадры, снизу крупные планы. Подписи добавлены после рендера.
Три образа одного кадра: A — оригами, B — мультяшный, C — реалистичный. Сверху полные кадры, снизу крупные планы. Подписи добавлены после рендера.

Я выбрал C. После этого пропорции и окрас зафиксировали, менялись только свет и окружение: тёплый контровой свет заката по краю силуэта, шерсть плотнее (шаг кусочков сократили с 7 до 5,6 пикселя), ещё десять елей на холмах, каменный порог с пеной у озера. Правки включаются отдельным флагом, поэтому прежние A, B и C по-прежнему пересобираются байт в байт.

Следующим шагом была проба ходьбы: 9 секунд, 270 кадров, без звука, терьер идёт по мостику. Потом первое демо из трёх сцен — мост, осенний лес, снежные горы — с переходами «рваной бумагой» и концовкой с логотипом. Демо мне понравилось, и дальше работа шла над движением и звуком.

Проба ходьбы: 12 кадров из девятисекундного ролика. Камера плавно едет вправо, слои сдвигаются с разной скоростью.
Проба ходьбы: 12 кадров из девятисекундного ролика. Камера плавно едет вправо, слои сдвигаются с разной скоростью.

Откуда берётся бумага

Ощущение бумаги складывается из трёх частей: формы, фактуры и глубины.

Формы. Каждый элемент — векторный контур: плавные кривые (сплайны Catmull-Rom) и многоугольники, у которых край слегка дрожит, как после ножниц. У каждого кусочка три составляющие: мягкая тень на нижний лист, заливка с градиентом от света и фаска в 1–2 пикселя — тёплый блик на освещённом крае и тёмный кант на дальнем. Так нарисованы облака, ели, камни моста и каждая ромашка.

Фактура. Поверх каждого слоя работает шейдер на GLSL — небольшая программа для видеокарты, которая считает цвет каждого пикселя; на таких же программах работают готовые эффекты для сайтов. Он добавляет «зуб» бумаги из шума, волокна, мягкое замятие, акварельную пятнистость пигмента и мелкий спекл. Рельеф освещён тем же солнцем, что и сцена, а дальние слои затянуты дымкой воздушной перспективы. Второй, финальный шейдер отвечает за ореол звезды, свечение солнца, виньетку и зерно. Там же живут переход «рваная бумага» с тенью нового листа и снегопад.

Глубина. Сцена с мостом собрана из 15 слоёв, как театральная диорама. Самый дальний — небо на расстоянии 100 условных единиц от камеры. Дальше облака, звезда, горы, лес с озером, холмы, ели, ручей, мост, терьер, валуны и два плана цветов; ближний стоит на расстоянии 13. Каждый слой — плоскость в трёхмерной сцене three.js перед перспективной камерой. Размеры плоскостей подобраны так, что в первом кадре слои совпадают пиксель в пиксель и картинка выглядит плоской. Стоит камере сдвинуться, и ближние слои уезжают быстрее дальних: появляется настоящий параллакс. В пробе ходьбы передний план за ролик смещается примерно на 170 пикселей, небо — примерно на 22.

Такую технику называют 2.5D: пространство у сцены объёмное, а все предметы в нём плоские, как вырезанные листы. В лесу и в снегу по 10 слоёв, в снежной сцене финальной версии к ним добавлен слой пыли.

Все 15 слоёв сцены с мостом по отдельности: от неба на заднем плане до крупных цветов на переднем.
Все 15 слоёв сцены с мостом по отдельности: от неба на заднем плане до крупных цветов на переднем.

Почему лапы не скользят при ходьбе

Самая заметная ошибка рисованной ходьбы — лапа стоит на земле, но едет вместе с телом. Глаз ловит такое сразу, даже если зритель не может объяснить, что не так.

Терьер собран как кукла на шарнирах: голова, уши, хвост, веки и корпус — отдельные куски со своими осями вращения. Каждая лапа состоит из двух звеньев и стопы: плечо или бедро, запястье или скакательный сустав, стопа. Углы суставов считает обратная кинематика (IK): программе говорят, где должна стоять стопа, и она сама вычисляет, как согнуть лапу.

Походка — латеральная четырёхтактная: лапы переставляются по очереди, заднее — переднее с одной стороны, потом с другой. Каждая лапа стоит на опоре 62 % цикла. Главное решение касается фазы шага. Если вести её по часам, тело и лапы живут в разных ритмах, и при любой смене скорости опорная лапа начинает скользить. Бот считает фазу по пройденному пути: опорная лапа уходит назад ровно на столько, на сколько корпус ушёл вперёд. Сверху добавлены детали, которые делают персонажа живым: корпус дважды за цикл слегка покачивается, хвост виляет, уши подёргиваются, терьер моргает и дышит.

В третьей версии появились прыжки. Каждый разбит на фазы: присед, толчок, полёт по параболе, приземление, отскок и успокоение. В лесу два прыжка, высотой 128 и 92 пикселя. На толчке тело вытягивается (0,94 по ширине и 1,09 по высоте), на приземлении сплющивается (1,10 и 0,86) — классический мультипликационный приём сжатия и растяжения. Тень остаётся на земле и в полёте уменьшается. Опорные лапы пересчитываются в координаты сцены с учётом наклона корпуса, иначе при наклоне они проваливались бы в землю.

В снегу терьер разгоняется с 56 до 220 пикселей в секунду, и шаг плавно переходит в галоп. Цикл удлиняется, опора сокращается с 62 до 32 % цикла, корпус наклоняется вперёд и раскачивается: нос вверх на толчке задних лап, нос вниз на ударе передних. Появляется фаза полёта, когда все четыре лапы в воздухе. Камера догоняет собаку с задержкой в полсекунды, поэтому параллакс ускоряется вместе с ней. Следы и снежная пыль появляются в моменты касания лап, по расчёту походки.

Шероховатости бот записал в отчёт сам. На стыках «прыжок — шаг» и «шаг — галоп» лапы немного скользят: здесь смешиваются позы и меняются фазовые сдвиги лап. Снежная пыль на белом снегу видна слабо. Галоп получился мультяшным: при 220 пикселях в секунду частота шага около 1,6 Гц, у настоящей собаки она выше.

Финальная версия по кадрам: шаг на мосту, два прыжка в осеннем лесу, галоп в снегу, растворение в бумагу и логотип.
Финальная версия по кадрам: шаг на мосту, два прыжка в осеннем лесу, галоп в снегу, растворение в бумагу и логотип.

Монтаж по ударам бочки

Звук собран по сетке трека. Музыка — «A Game» автора Michael Ramir C из бесплатной библиотеки Mixkit, 128 ударов в минуту: одна доля длится 0,46875 секунды. Бот разметил сетку долей и поставил на сильные доли все события картинки. Переходы между сценами стоят на 9-й и 18-й доле. Присед, толчок и приземление первого прыжка — на 12, 13 и 14-й, второго — на 15,5, 16 и 17-й. Растворение в бумагу идёт с 27-й по 29-ю долю, логотип садится на 30-ю. Попадание проверено замером: в полосе ниже 120 Гц, где звучит бочка, энергия на доле выше, чем между долями, на 10–26 дБ для каждого события.

Шумы — настоящие записанные эффекты Mixkit, по одному на событие: шорох песка на переходах, свист воздуха на взлётах, короткий басовый удар на приземлениях, порыв воздуха, когда галоп набирает скорость, и лопающийся пузырь на логотипе. Синтезированных звуков нет. Звуков шагов в библиотеке не нашлось, поэтому шаги беззвучные. Громкость финального ролика — −16,1 LUFS.

Между первым демо и финалом была ещё одна версия. Для первого демо бот выбирал трек анализом: детской музыки в библиотеке тогда не было, и из 18 треков он взял самый «мажорный» по анализу тональности — бодрый EDM. Я попросил версию с детской музыкой. Бот взял трек «Kidding Around» из той же библиотеки и заменил только звук: видеопоток скопировал из первого демо без перекодирования. Новый трек идёт в темпе 87,7 удара в минуту, а переходы в картинке разнесены на 7,5 секунды. Это 10,96 доли, поэтому оба перехода одновременно на долю попасть не могут. Логотип сел точно в долю, переходы — мимо.

Мы попробовали этот вариант и поменяли план. Я захотел третью версию. В ней вернулась «A Game», сцены стали короче, и у каждой появилось своё движение: шаг на мосту, прыжки в лесу, бег в снегу. Ролик сократился с 26,7 до 15,5 секунды. После всех правок контрольные кадры первого демо совпали с исходными до байта: новая версия живёт рядом со старой и ничего в ней не ломает.

Что можно повторить и чем

Весь стек доступен бесплатно:

  • three.js — 2.5D-сцена из слоёв, перспективная камера и шейдеры;
  • Canvas2D — рисование форм прямо в браузере;
  • GLSL — фактура бумаги и финальная обработка кадра;
  • puppeteer-core и headless Chrome — покадровый рендер страницы в PNG;
  • ffmpeg — сборка кадров и звука в MP4;
  • Mixkit — музыка и шумы по бесплатной лицензии. У неё есть ограничения: трек нельзя использовать на ТВ и радио, в играх, на физических носителях и регистрировать в Content ID.

Повторять стоит в первую очередь принципы: они пригодятся и в соседнем способе делать видео кодом — на Remotion со скиллом video-shotcraft.

Кадр — функция времени. Функция window.render(t) рисует кадр только по значению времени: без случайных чисел, без системных часов, без requestAnimationFrame. Все «случайные» детали берутся из генератора с фиксированным зерном, у каждого слоя своё. Поэтому два независимых рендера дают побайтово одинаковые PNG, а любую правку можно проверить сравнением контрольных сумм. Страница собрана как композиция в формате HyperFrames, и render(t) можно вызывать из его мастер-таймлайна.

Отсутствие картинок доказывается журналом. Локальный сервер рендера отдаёт только .html и .js, на всё остальное отвечает ошибкой. Страница во время рендера записывает каждый запрос, и после прогона видно, сколько из них было изображениями, шрифтами или медиа.

Движение считается от пространства, события — от музыки. Фаза шага привязана к пройденному пути, следы — к касаниям лап, монтаж — к сетке долей.

Цена у подхода тоже есть. Код занял около 2 700 строк: только сцена моста — 723, риг терьера — 375. Рендер небыстрый: кадр считается около 4 секунд на программной графике SwiftShader. Проба ходьбы из 270 кадров заняла 17 минут. Финальную версию бот рендерил в два параллельных процесса по 204 кадра, каждый около 13–14 минут, плюс концовка с логотипом меньше чем за минуту. Если сцене нужен настоящий объём с моделями и светом, ближе другой путь — первая 3D-сцена в Blender через Codex.

Если хотите попробовать сами, вот задание для агента, с которого можно начать:

Промпт:

Сделай короткую анимационную сцену в стиле вырезанной бумаги без единого файла изображения.

Стек: одна HTML-страница, JavaScript (ES-модули), Canvas2D для форм, three.js для сцены и шейдеров. Рендер покадрово в headless Chrome через puppeteer-core, сборка в MP4 через ffmpeg.

Требования:
1. Все формы рисуй кодом: сплайны и многоугольники с чуть неровным краем, у каждого кусочка мягкая тень на нижний лист, заливка с градиентом от света и тонкая фаска.
2. Фактуру бумаги сделай GLSL-шейдером на каждом слое: зерно из шума, волокна, лёгкое замятие, пятнистость пигмента, дымка на дальних слоях.
3. Собери сцену из 10–15 плоских слоёв на разной глубине перед перспективной камерой так, чтобы в первом кадре они совпадали, а движение камеры давало параллакс.
4. Напиши функцию window.render(t), которая рисует кадр только по времени t: без Math.random, Date и requestAnimationFrame. Случайность бери из генератора с фиксированным зерном.
5. Собери персонажа из отдельных частей с осями вращения. Лапы сделай двухзвенной обратной кинематикой, фазу шага считай от пройденного пути.
6. Локальный сервер рендера должен отдавать только .html и .js. Запиши все сетевые запросы страницы и проверь, что запросов изображений, шрифтов и медиа ноль.
7. Отрендери один кадр дважды и сравни контрольные суммы PNG: они должны совпасть.

Сцена: персонаж идёт слева направо по мостику на закате, камера медленно едет вправо.

Известные шероховатости в ролике остались: лапы чуть скользят на стыках движений, галоп мультяшнее настоящего, пыль в снегу почти не видна. Бот их описал сам, и в коде видно, где каждую править.


Следующий шаг

База знаний: HyperFrames от HeyGen — видео как код для ИИ-агентов и Codex — как устроено видео, которое собирается из HTML и кода, и как поставить такую задачу агенту.

Если вы делаете видео для продукта или канала и прикидываете, какую часть работы можно отдать коду и агенту, этот подход можно разобрать на ваших задачах.

Если захотите обсудить, как это применить у себя или в команде — пишите в Telegram @pimenov