Нотатки розробки
Новела складається із зображень. Чому конструктор виявився таким складним?
Як невеликий прототип Fibber перетворився на розподілену платформу зі спільною роботою, версійною публікацією та єдиним runtime історій.
Ззовні візуальна новела здається майже елементарною: фон, персонаж із прозорістю, вікно з текстом, кілька варіантів вибору з переходами до наступних сцен і музика. Саме так я й міркував, коли починав першу версію.
Я й досі вважаю, що ядро новели просте. Сьогодні, особливо разом з AI, майже будь-який розробник може створити плеєр візуальної історії. Складність починається, коли потрібно зробити не одну новелу, а інструмент, у якому інша людина, можливо навіть дитина, зможе створити, перевірити, опублікувати й далі розвивати власну історію.
У першої версії було одне завдання
До того як Romergo отримав свою назву, він був Fibber. Він збирав один квест із пов’язаних сцен, давав змогу наповнювати їх текстом і зображеннями та підтримував сюжетні розгалуження. Основою були TypeScript, React, Strapi й Ant Design.
Ця версія виконувала своє завдання. Граф робив історію видимою, сцени можна було редагувати, а результат запускати в попередньому перегляді. Для прототипу цього вистачало. І цього ж вистачило, щоб побачити справжню проблему.
Першим справжнім вузьким місцем був не код
Коли ми почали наповнювати справжній квест текстом і зображеннями, складання десятихвилинної сцени могло тривати майже цілий день. Створи сцену. Завантаж фон. Завантаж персонажа. Розмісти їх. Додай репліки. З’єднай із наступною сценою. Повтори.
Частково це була проблема інтерфейсу. Я покращував UX, пришвидшував завантаження й робив публікацію надійнішою. Але найбільша витрата залишалася незмінною: кожну дрібну дію людина все ще мала виконувати вручну.
А що, як над історією зможуть одночасно працювати двоє?
Перша відповідь була простою: якщо одна людина стала вузьким місцем, потрібно дати кільком людям змогу працювати над тією самою історією. Спочатку я прийшов до Liveblocks, вивчив цю модель, а згодом почав будувати власний шар спільної роботи на основі Yjs.
У демонстрації нижче два вікна редактора просто змінюються синхронно. Але під цим ховається значно важливіша зміна: правки стають оновленнями спільного документа, а не окремими надсиланнями форм. Звідси виникають присутність учасників, повторні підключення, розв’язання конфліктів, права доступу й очікування, що проєкт завжди залишається живим.
Кожне корисне скорочення шляху зрештою ставало межею
Strapi був чудовим способом швидко отримати backend, але кожна нова функція для інтерактивних історій змушувала CMS поводитися дедалі менше як CMS. Ant Design дав першому інтерфейсу швидкість і послідовність, а потім поступово почав обмежувати продукт чужою візуальною системою.
Пізніша serverless-ітерація на Remix стала важливим кроком уперед. Проте обране поєднання застосунку, медіа й розгортання не давало горизонтальної свободи, якої вже потребував продукт. Жодне з цих рішень не було помилкою: кожне давало мені час, щоб побачити наступне обмеження.
Зображення не просто вкладення. Це інфраструктура.
Медіа швидко стали окремим уроком. Зображення для історії треба один раз завантажити, за потреби перетворити, надійно зберегти й швидко віддати будь-якому гравцеві у світі. Зберігати все поруч із сервером застосунку, що вже зростав, означало ще більше навантажувати backend, тому першим окремим рішенням став Cloudinary.
Одного вечора я відкрив його панель і побачив, що один користувач витратив близько 500 МБ, сотні разів завантаживши те саме зображення. Моє термінове виправлення порівнювало хеші й відхиляло дублікати. Це працювало, але ліміти запитів і квоти вирішили б проблему пряміше. Ще більше здивував трафік: один вечір тестування міг витратити понад десять відсотків безкоштовного ліміту.
Цей випадок змінив модель. Медіа більше не могли бути полем сцени. Їм був потрібен власний pipeline для зберігання, дедуплікації, доставки, кешування й доступу.
Як проста ідея виглядає сьогодні
У сучасному Romergo Builder і плеєр розділені, але використовують спільний runtime історії. Попередній перегляд у редакторі й опубліковане проходження підкоряються однаковим правилам, тому випадково порушити контракт значно складніше.
Yjs синхронізує редагований проєкт між учасниками. Публікація створює перевірений snapshot даних історії та її медіа, тому автори можуть змінювати чернетку, не підмінюючи версію, у яку вже грають. API побудовано на Hono, інтерфейс використовує примітиви shadcn і Radix, а хмарний шар відповідає за розподілену координацію, об’єктне сховище й доставку через CDN.
Спільний runtime переносить ту саму історію до браузера й PWA, Telegram і синхронного проходження в Discord. Сумісність версій, офлайн-режим і стан групової гри тепер не побічні ефекти навколо сторінки, а самостійні продуктові системи.
Поточна архітектура
Одна редагована історія. Один опублікований контракт.
Узагальнена схема поточної архітектури Romergo.
1. Редагований проєкт
- Builder UI: Сцени · граф · медіа
- AI / MCP: Авторизовані операції редагування
- Realtime / Yjs: Durable Objects · спільний робочий документ
- Editor API: CAS-запис · матеріалізація чернетки
- Draft runtime snapshot: PublishedQuestLocalizedContentV2 · D1 / R2
- Медіа чернетки: Об’єкти R2 · метадані D1
2. Версійна публікація
- Перевірки публікації: Схема runtime · підтвердження автора · наявність медіа
- Незмінна публікація vN: Runtime snapshot · скопійовані й перепризначені медіа
- Контракт видимості: Публічна · за посиланням · приватна
3. Єдиний контракт виконання
- Snapshot чернетки: Останній редагований стан
- Публікація vN: Стабільний стан плеєра
- Спільний player-runtime: Сцени · переходи · умови · змінні · збереження
- Попередній перегляд Builder: Запускає чернетку в тому самому рушії
4. Канали відтворення
- Web / PWA: Плеєр у браузері
- Telegram Mini App: Вбудований плеєр
- Discord Activity: Синхронна групова гра
5. Платформна інфраструктура
- Clerk + OAuth: Ідентифікація й доступ MCP
- Hono Workers: Сервіси API та MCP
- Cloudflare D1: Проєкти · версії · метадані
- Cloudflare R2: Медіа · великі runtime snapshots
- Durable Objects: Кімнати Yjs · сесії Discord
- Прогрес гравця: Синхронізація D1 · локальна офлайн-черга
Новела й досі складається із зображень
Кумедно, що початкове припущення збереглося. Візуальна новела й досі складається з фонів, персонажів, тексту, виборів і звуку. Romergo став складним, бо все навколо цього простого ядра мало стати надійним: спільна робота, медіа, публікація, версії, офлайн-гра й кілька способів пережити ту саму історію.
Якщо ця еволюція чогось мене навчила, то вимірювати весь людський шлях, а не лише код, що показує фінальний екран. Плеєру може вистачити п’яти базових елементів. Продукту, який допомагає перетворити ідею на завершену ігрову історію, потрібна система.