лаборатория · beelink-rive-lab · 2026-08-26

Марья,
собранная кодом

Один нарисованный кадр превращается в .riv с двадцатью деталями, восемью мешами, скелетом из тридцати костей и циклом ходьбы. Ни одного клика в редакторе Rive: официального экспортёра не существует, файл пишется питоном по открытым определениям формата. Ниже — тот самый файл, играющий настоящим рантаймом Rive прямо в этой вкладке.

поднимаем рантайм… · @rive-app/canvas

кадр
время
файл
объектов
18 794

Файл едет к вам целиком — вместе с восемью вложенными PNG. Всё, что вы видите, считает wasm-рантайм Rive у вас в браузере: сервер отдаёт статику и больше ничего не делает.

Как это получается

Восемь шагов от рисунка до файла. Инструмент написан один раз; на каждого следующего персонажа повторяются шаги 1–8, и все они бесплатные — деньги тратит только рисование рефа.

  1. Реф в рабочей позе

    Вход всего пути — один рисунок, не лист кадров. Фон хромакеем, разрешение с запасом (1696×2528 при игровом кадре 576×624), поза рабочая: риг умеет ±90° вокруг нарисованного положения.

  2. Скелет объявляется по рисунку

    Единственный ручной вход конвейера — двадцать строк данных: полилиния кости, радиус, родитель, порядок по глубине. Координаты снимаются с самого рефа: он в аниме-пропорциях, и конвейерный скелет резал бы по костям, которых на картинке нет.

  3. Нарезка на детали

    Деталь — трубка переменного радиуса вдоль своей кости, обрезанная силуэтом. Скрытое (39% площади, у шеи 88%) закрашивается ближайшим видимым пикселем той же детали. Двадцать PNG и карта пивотов.

  4. Доказать, что карта не врёт

    Детали ставятся обходом дерева и обязаны собраться обратно в реф. Мировых координат в карте нет нарочно — иначе сборка проверяла бы сама себя. IoU 0.9901, цвет 0.9607.

  5. Сетка и развесовка — числом, не на вкус

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

  6. Цикл движения — из канона

    Позы не сочиняются: берётся FROZEN["walk"] боевого конвейера — мокап CMU с ретаргетом на длину шага движка.

  7. Сборка .riv

    Деталь → Image + Mesh + MeshVertex; скелет → RootBone/Bone; привязка → Skin + Tendon + Weight; поза → LinearAnimation + KeyFrameDouble. Полторы секунды на файл.

  8. Доказать пикселями настоящего рантайма

    Судья — chromium, а не наши расчёты: у node-дубля нет WebGL, и без него image mesh не рисуется вовсе — оба кадра вышли бы одинаково пустыми и совпали бы идеально. Плюс шесть контрольных файлов, каждый обязан провалиться.

Чем это доказано

У каждого утверждения — отрицательный контроль: файл, который обязан провалить ту же проверку. Утверждение без такого контроля в лаборатории не считается доказанным.

утверждениекак доказаноотрицательный контроль
персонаж ходит5 пар соседних кадров расходятся на 21.5–22.1%цикл замкнут побитово: 0 px
порядок всех 20 деталей по картецвет 0.9560 против эталона из карты--zflip 0.8084 и --tflip 0.8796
скелет — одно деревокорень тянет все 8 мешей, 19.7% кадрау восьми корней меши стояли бы
привязка работаетвершины ездят--unbind: 0 различающихся пикселей при непустом кадре
сгиб локален1676 px у колена, 0 у бедралинейная рампа: отношение 6.71
наша математика вернамодель предсказывает кадр рантайма, цвет 0.9810предсказание для другого момента краснеет

Измеренные числа

Таблица не переписана руками: она разбирается из README.md репозитория, где каждая строка пересчитывается воротами из артефакта прогона. Подделанная цифра красит ворота.

величинаизмеренооткуда число
загружается…

Что оказалось неправдой

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

«Силуэт покажет, что персонаж собран правильно»

Нет. Силуэт слеп к порядку отрисовки полностью: IoU 0.9956 у правильного файла, 0.9956 у файла с перевёрнутым потоком и 0.9956 у файла с перевёрнутой склейкой текстур — до четвёртого знака. Ловилось только цветом (0.9560 против 0.8084) и только сверкой с эталоном, построенным не из проверяемого файла. Три раза одна ошибка, три раза одни цифры.

«Первый в потоке рисуется первым»

Наоборот: Rive рисует drawables артборда задом наперёд, как редактор рисует список слоёв. Собранный «по возрастанию z» файл дал дальний рукав поверх груди — 18 977 пикселей из 409 600 при том же силуэте. Это и есть третья вкладка демонстрации выше.

«Формат читается по одним публичным defs»

Наполовину. Номера — да, 371 объект и 3368 свойств. Но правил провода там нет вовсе: bool — сырой байт, Color — 4 байта без varuint, битмап оглавления идёт по 4 ключа на слово uint32, а не по 16. Плюс старые номера свойств: без них не читается больше половины публичных файлов. Источник правды — открытый C++ рантайма, документация в одном месте прямо вводит в заблуждение.

«Убитый прогон мутаций ничего не оставляет»

Оставляет. SIGKILL идёт мимо finally, и порча уехала в коммит вместе с журналом, написанным по числам чистого сборщика: независимый прогон дал 62 из 64 там, где отчёт заявлял 64 из 64. Правило: мутации не запускать в фоне, а отчёт агента считать заявкой, а не фактом.

«Проверить .riv можно в node»

Нельзя, если в файле картинки: загрузка там висит вечно — промис ждёт декодирования ассетов через createImageBitmap, которого в node нет. Вторая половина беды: у node-канваса нет WebGL, и рантайм честно печатает «Image mesh will not be drawn» — оба кадра вышли бы одинаково пустыми и совпали бы идеально. Авторитет по мешам — только браузер.

«Заглушка под перекрытыми кусками лечится только деньгами»

Так было записано, когда сгиб обоих локтей на 90° обнажал плоскую растяжку на 19.4% видимой площади торса. На целом персонаже в ходьбе вся заглушка кадра не превышает 1.12%: рука качается на ±6°, а не складывается вдвое. Цифра 19.4% была верной только для движения, которое тот этап себе сочинил. Дорисовка за деньги на походку не нужна.

«Ветвление скелета просто работает»

Не работает: у дочерней кости в Rive нет своих координат, x() = parent->length(). Десять костей-связок — не украшение, а единственный способ записать дерево карты, не двигая суставы под формат.

«Ворота офлайна работают»

Были декоративными: проба била в example.invalid, а это зарезервированный TLD — он не резолвится нигде и без всякого запрета. Отсюда общее правило лаборатории: у любого отрицательного утверждения проба обязана уметь дать положительный ответ в мире, где утверждение неверно.

Вердикт

За что путь берут

  • Один рисунок вместо листа кадров.
  • Файл вчетверо легче листа: 1 МБ против 4.2 МБ.
  • В тридцать раз легче по текстурной памяти: 4.36 МиБ против 142.6 МиБ.
  • Любое движение добавляется кодом за минуты и без единого доллара.
  • Качество деформации доказано числами, а не глазами.

Чего он стоит и чего в нём нет

  • Новый рантайм в проекте, где сейчас PixiJS: +1.8 МБ wasm на клиента.
  • Проверен ровно один цикл движения — кит ударов впереди.
  • Диапазон ±90° вокруг нарисованной позы. Это ограничение рефа, а не формата, и лечится рисованием.
  • Плоский меш не умеет самоперекрытий: ни рука за спину, ни нога накрест.
  • Просадка объёма в суставе 7.85% — линейный блендинг, другого в Rive нет.

Для локомоции путь рабочий и его стоит брать; для кита ударов — не раньше, чем появится реф в нейтральной стойке. Практический ход: следующего персонажа рисовать рефом в нейтральной стойке и пройти по этому конвейеру целиком — тогда станет видно, снимается ли ограничение №2, а это единственный вопрос, который решает судьбу всего пути.