scroll-video-website: скилл, который делает из видео сайт, управляемый прокруткой
📂 Исходный код на GitHubСкилл в портативном формате SKILL.md, который превращает любой ролик в сайт, прокручиваемый как видео: нарезка на кадры WebP через ffmpeg, плавная перемотка на canvas в обе стороны, постепенная подгрузка кадров и отдельный режим оптимизации с измерением экономии. MIT-лицензия.
Сайт, который открывается как видео: колесо мыши не двигает страницу мимо картинки, а перематывает ролик. Именно так устроены страницы продуктов Apple. Этот приём упакован в агентный скилл scroll-video-website: вы даёте агенту путь к видео, он нарезает ролик на кадры WebP и собирает страницу, в которой прокрутка играет роль пульта управления.
Это не библиотека и не фреймворк, а файл SKILL.md в портативном формате Agent Skills. Такую папку одинаково понимают Claude Code, Codex и любой другой агент, который работает с этим форматом. Репозиторий создан в августе 2026, лицензия MIT, 43 звезды, последние правки — сентябрь 2026.
Что делает скилл
Скилл закрывает одну задачу: сделать страницу, где анимация — это интерфейс, а не фон. Ни шапки, ни карточек, ни кнопок «купить», ни выдуманного рекламного текста. Страница работает как полотно во весь экран, а прокрутка им управляет.
Возможности из README:
- видео превращается в оптимизированную последовательность кадров WebP;
- прокрутка идёт в обе стороны плавно, а кадры подгружаются по мере надобности;
- canvas растягивается под любой экран, а соседние кадры плавно смешиваются между собой;
- скилл не ломает уже работающий стек проекта и уважает системную настройку «уменьшить движение»;
- по умолчанию собирается страница, где есть только canvas, а ваши указания по стилю выполняются поверх этого;
- готовую нарезку можно отдельно оптимизизовать, и скилл покажет, сколько веса освободит каждый вариант.
Как вызвать
Установка:
npx skills add musoyangrigor/scroll-video-website-skill --skill scroll-video-website
Дальше есть два вызова. Первый — путь к видео:
$scroll-video-website ./media/product-demo.mp4
Всё, что стоит после пути к файлу, агент читает как пожелание по оформлению:
$scroll-video-website ./media/product-film.mp4 use a dark editorial style
Отдельного ключевого слова use не требуется. Можно написать просто «тёмный редакционный стиль» — агент поймёт это как указание по стилю. Второй вызов — optimize, о нём ниже.
Относительный путь агент читает от текущей рабочей папки. Путь с пробелами надо заключить в кавычки. Если голое имя файла не найдено, агент ищет точное совпадение по всему проекту и не заглядывает в папки зависимостей, сборки, кэша и системы контроля версий. Нашлось ровно одно совпадение — агент продолжает сам. Ноль или несколько совпадений — агент спрашивает точный путь. Слово optimize — это отдельный режим работы, а не имя файла.
Как видео превращается в кадры
Основной путь — не video.currentTime, а нарезка на пронумерованные кадры WebP с именами вида frames/frame_0001.webp.
Правила из SKILL.md:
- частота кадров сохраняется до 30 fps; для длинного ролика нарезка прореживается примерно до 240–300 кадров, чтобы вес страницы оставался разумным;
- пропорции кадра сохраняются; качество WebP берётся в районе 78–82, и результат потом осматривают глазами, а не считают, что этого значения хватает;
- фактическое число извлечённых кадров записывается в код. Придумывать константу вместо реального числа запрещено;
- извлекает кадры
ffmpeg:
ffmpeg -i input.mp4 -vf fps=30 -c:v libwebp -quality 80 frames/frame_%04d.webp
- если извлекать кадры нечем, скилл разрешает перейти на пошаговую перемотку остановленного видео, но только с объяснением, чем это хуже.
Скилл не рисует собственную графику и не подменяет присланный ролик. Перед началом работы он осматривает проект и переиспользует уже работающий стек, а не разворачивает новый проект поверх существующего. Параметры ролика (размеры, длительность, частота кадров, вес) снимает утилита ffprobe, если она есть.
Как устроена прокрутка
Страница-полотно: белый фон, зафиксированная сцена размером 100vw на 100vh, overflow: hidden. Чтобы прокрутка была осмысленной, документ делают высоким — около 400vh на восьмисекундный ролик. Canvas помечен aria-hidden: он декоративный, и программам чтения с экрана его читать не нужно.
Общий прогресс страницы превращается в номер кадра:
const range = document.documentElement.scrollHeight - innerHeight
const progress = range > 0 ? Math.min(1, Math.max(0, scrollY / range)) : 0
targetFrame = progress * (frameCount - 1)
Дальше работает единственный цикл requestAnimationFrame. Номер кадра не прыгает сразу к целевому, а плавно к нему подходит. Сглаживание сделано через экспоненту и не зависит от частоты кадров:
const dt = Math.min((now - lastTime) / 1000, 0.1)
const alpha = 1 - Math.exp(-dt * 9)
currentFrame += (targetFrame - currentFrame) * alpha
Так колесо мыши не дёргает картинку рывками, а прокрутка вверх честно отматывает видео назад.
На каждом шаге рисуются сразу два кадра: целый под указателем и следующий за ним. Второй дорисовывается поверх первого с прозрачностью, равной дробной части указателя. Переход получается плавным, без рывка в тот момент, когда номер кадра округляется.
Обработчики скролла и изменения размера окна делают только простые замеры и обновляют целевой кадр — вся отрисовка живёт в одном цикле. Буфер canvas подстраивается под плотность пикселей экрана, но не выше примерно ×2, а измерения ведутся в CSS-пикселях. При изменении размера окна трансформация сбрасывается, иначе масштаб будет накапливаться при каждой перерисовке.
Как кадры подгружаются
Первая отрисовка не должна ждать всю нарезку. Порядок такой:
- грузится кадр 1, рисуется, и только после этого canvas становится видимым — иначе мелькает пустой белый прямоугольник;
- в очередь ставятся кадры через один (каждый восьмой), равномерно по всей ленте, чтобы в начале проскролливания рядом всегда был запасной кадр;
- потом в очередь встаёт всё остальное.
Запрошенные кадры и загруженные считаются отдельно — иначе второй и третий проходы запросят одну и ту же картинку дважды. Пока кадр грузится, рисуется ближайший уже загруженный. Неудачная загрузка одного кадра не должна останавливать остальные.
Страница по умолчанию
Если пользователь не описал оформление, на выходе получается почти пустая страница: canvas во всю сцену, изображение вписывается по принципу cover и центрируется по обеим осям. Ни заголовков, ни градиентов, ни призывов к действию. Появление через прозрачность короткое и начинается только после готовности первого кадра. Если ролик снят на белом фоне, скилл может добавить белую подложку, лёгкую правку контраста или mix-blend-mode: multiply.
Контент на страницу добавляют только по просьбе и только в заранее выбранные моменты анимации, чтобы текст не спорил с canvas за внимание. Библиотеку анимации при этом не подключают: встроенных возможностей canvas и браузера хватает.
Второй режим: optimize
Команда $scroll-video-website optimize не принимает аргументов и запускает другую процедуру. Она описана отдельно в references/optimize.md.
Скилл ищет в проекте пронумерованные нарезки, мерит реальные файлы (формат, число кадров, размеры, использование прозрачности, общий вес в байтах), находит в коде то место, где задаются адреса кадров и их количество, и проверяет, какие кодировщики есть локально.
Дальше идут оценки на настоящих данных. Скилл берёт 24 кадра, равномерно размазанных по всей ленте, кодирует их каждым вариантом настроек и по результату прикидывает, сколько весила бы вся нарезка. Для очень короткой или очень разной нарезки число образцов меняется.
Отдельно оцениваются четыре вещи:
- формат — например, переход с WebP на AVIF;
- число кадров — сколько кадров оставить на всей ленте;
- качество сжатия — несколько уровней в текущем или выбранном формате;
- прочие настройки, которые нашлись при осмотре: максимальный размер картинки, режим без потерь, субдискретизация цвета (как часто сохраняются цветовые каналы), глубина кодирования, удаление метаданных.
Каждый вариант показывается отдельной строкой с цифрами:
Текущий размер: 2.8 MB → Ожидаемый размер: 1.3 MB (~54% меньше)
Проценты экономии нельзя складывать и умножать: кодек, качество, размеры и отбор кадров влияют друг на друга. Поэтому после выбора настроек скилл прогоняет их вместе на тех же образцах и показывает один итог:
Все выбранные оптимизации вместе
2.8 MB → 0.7 MB (~75% меньше)
Только после подтверждения начинается обработка. Просьба «посмотри и прикинь» не даёт права переписывать файлы.
Замена делается через временную соседнюю папку. Сначала собранную последовательность проверяют: число кадров, размеры, читаемость, порядок, поведение прозрачности, первый и последний кадр, общий вес. И только после проверки новая нарезка подменяет старую. Старая остаётся на месте для отката, если пользователь не попросил её удалить. Пути, расширения, манифесты, списки предзагрузки и константы с числом кадров обновляют одним заходом. Исходный ролик и посторонние файлы не трогают.
Если нарезки в проекте нет, скилл отвечает Frames folder was not found. и останавливается. Он не просит путь и не пытается оптимизировать чужие картинки.
Телефоны и доступность
На узких экранах cover остаётся: кадр обрезается по краям, но не растягивается и не искажается. Если на странице появился видимый контент, учитывают то, что на телефоне меняется высота окна, и отступы от краёв экрана, которые требуют новые модели смартфонов.
Для системной настройки prefers-reduced-motion: reduce прокрутка видео выключается, а на экране остаётся один подходящий кадр. Текст страницы остаётся доступным, а прокрутку страницы скилл не перехватывает. Все подписки на события и кадры анимации отменяются, когда блок исчезает со страницы.
Что проверить после сборки
Финальный чек-лист из SKILL.md:
- первая и последняя позиция прокрутки действительно дают первый и последний кадры;
- прокрутка вперёд и назад идёт непрерывно, без рывков, проигрывания, мигания и пустого canvas;
- кадры не запрашиваются дважды, а на месте отсутствующих рисуется ближайший загруженный;
- обрезание
cover, масштабирование под плотность пикселей и пересчёт при изменении размера окна работают и на десктопе, и на телефоне; - при
prefers-reduced-motionкадр статичен, а слушатели сняты; - число, размеры, порядок и общий вес нарезки совпадают с ожиданием;
- остальное поведение проекта не сломано.
В конце работы скилл пишет короткий отчёт: что получилось, сколько кадров извлечено и каким весом, какие проверки прогнаны и какие ограничения остались у исходного ролика.
Когда скилл не нужен
Обычные видео-фоны на автоплее и любые скролл-эффекты, не связанные с видео. Скилл нужен, когда прокрутка должна именно перематывать ролик, а не просто двигать страницу мимо картинки.
Что лежит в репозитории
- SKILL.md — основной сценарий сборки страницы;
- references/optimize.md — сценарий оптимизации готовой нарезки;
- agents/openai.yaml — подсказка по умолчанию для Codex;
- assets/demo.gif — демонстрация работы;
- LICENSE — MIT.
Скилл настолько узкий, что его логика целиком помещается в один текстовый файл. Ни npm-пакетов, ни рантайма, ни сборки: агент читает инструкции и сам пишет код под стек вашего проекта. Если вам нужна именно эта механика прокрутки как перемотки видео, скилл экономит несколько часов на подбор сглаживания, порядка загрузки кадров и обхода типичных поломок на телефоне.