Как я создал браузерную игру за 2 часа 37 минут, разделив задачи между Fable 5.2 и Opus 5.5
Недавно обсуждали интервью HR-директора крупной IT-компании: принимая сложные решения, она прислушивается к ощущениям в теле. Примерно тогда же вышел Fable 5.2. Я решил воспользоваться поводом, испытать модель и создать игру о собеседовании в вымышленной корпорации, где оффер выдают по натальной карте.
От первого сообщения модели до запуска игры на собственном домене прошло 2 часа 37 минут. Рассказываю, как распределял задачи между моделями, как устроен код, где возникли ошибки и что пришлось исправить вручную.
Игра: Rдкс, собеседование в корпорацию. Прохождение занимает 2–3 минуты. Игра работает в браузере и ничего не отправляет на сервер.
Что это за игра
Игра стилизована под российские FMV-квесты начала 2000-х: вместо графики — фотографии, вспышка прямо в лицо, резкие переходы от общего плана к крупному и намеренно некачественная озвучка.
Игрок указывает в анкете имя и дату рождения. Затем охранник проверяет его вайбометром: нужно остановить стрелку в зелёной зоне «бленд» между отметками «выгорел» и «слишком счастлив». В коридоре предстоит выбрать, к какой сотруднице отдела кадров обратиться. На собеседовании резюме отправляется в шредер. Наконец рекрутер составляет натальную карту и предлагает одну из 25 должностей либо отказывает: в новолуние корпорация никого не нанимает.
В игре семь финалов, включая один секретный. Корпорация выдумана, а персонажи сгенерированы.
Как распределялась работа
|
Роль |
Исполнитель |
Задачи |
|---|---|---|
|
Ведущий |
Fable 5.2, основная сессия |
концепция, визуальный стиль, сценарий, генерация изображений, тестирование в браузере, деплой |
|
Разработка |
два субагента на Opus 5.5 |
параллельная разработка движка расчёта по дате и движка сцен |
|
Звук |
скилл саунд-дизайна |
озвучка реплик через GPT Audio, локальная запись диктора, сведение |
|
Публикация |
скилл деплоя |
сборка образа, Dokploy, подключение домена с HTTPS |
Логика распределения была такой: Fable как фронтир-модель лучше всего справляется с творческими задачами — концепцией, тоном, визуальным стилем и разветвлённым сценарием. Opus хорошо пишет код, а доступные лимиты позволяют запускать сразу нескольких агентов. Sonnet я обычно использую для рутины и миграций, но в этом проекте он не понадобился.
Под скиллами здесь понимаются заранее подготовленные инструкции для конкретных задач: например, по генерации и сведению звука или размещению сборки на нашем сервере. Модель самостоятельно обращается к ним на нужном этапе.
Сначала концепция, потом код
Первые 56 минут мы потратили не на код, а на документ. Я надиктовал идею и предоставил два референса: скриншот FMV-игры начала 2000-х и рилс с «ночной экскурсией по цеху», снятой на мыльницу со вспышкой. Fable изучил материалы и подготовил концепцию из 10 разделов: тон и границы юмора, визуальный язык, сцены, финалы, формула расчёта, 25 должностей, список необходимых материалов, техническая часть и подзадачи.
В дальнейшем документ служил техническим заданием для субагентов. Я прочитал его, отредактировал сценарий и шутки и уточнил, как персонажи перемещаются по ленте сцен. После этого модели почти не задавали вопросов: вся необходимая информация уже была в документе.
Не все идеи из концепции вошли в игру. Например, предлагалось синхронизировать рот персонажа с громкостью реплики, меняя кадр восемь раз в секунду. От этого отказались: для пародии оказалось достаточно статичных поз и резкой склейки с переходом на крупный план лица.
Стек
-
Vite + TypeScript, без игрового движка. Сцена состоит из DOM-элементов.
-
Примерно 2 700 строк TypeScript, включая тесты.
-
Бэкенда нет. Прогресс и открытые финалы сохраняются в
localStorage. -
Аналитика — собственный Umami. Отслеживаются старт, сцены, прохождение или провал вайбометра, финал, шеринг и переход по ссылке.
Сценарий в формате данных
Ключевое архитектурное решение — хранить сценарий в JSON отдельно от движка диалогов. В script.json содержатся 50 узлов, 6 сцен и 7 финалов. Вот как выглядит узел с выбором:
"g_hey": {
"bg": "guard",
"speaker": "guard",
"text": "Эй. Сударь или сударыня?",
"choices": [
{ "text": "Сударь", "set": { "gender": "m" }, "to": "g_form" },
{ "text": "Сударыня", "set": { "gender": "f" }, "to": "g_form" }
]
}
Условные переходы тоже описаны в JSON, без вмешательства в код:
"g_check": {
"note": "Возрастной фильтр. Порог правится здесь.",
"speaker": "guard",
"text": "На собеседование. Ну-ну. Дай-ка анкету.",
"next": [
{ "if": { "ageGte": 50 }, "to": "g_old" },
{ "to": "g_vibe_intro" }
]
}
Такой подход заметно ускорил правки. Когда я просил ужесточить формулировку или изменить реплику, достаточно было поправить JSON. После первой сборки код движка почти не менялся.
Натальная карта по Миусу
Поскольку игра связана с гороскопами, хотелось рассчитывать карту по-настоящему. Субагент на Opus реализовал упрощённые вычисления по книге Жана Миуса «Астрономические алгоритмы»: долготу Солнца — по главе 25, долготу Луны — по основным членам таблицы 47.A. Точность составляет около 0,1°. Время рождения игра не запрашивает, поэтому расчёт ведётся на полдень по Москве.
export function julianDayNoonMsk(y: number, m: number, d: number): number {
return Date.UTC(y, m - 1, d, 9, 0, 0) / 86400000 + 2440587.5
}
const jd = julianDayNoonMsk(y, m, d)
const sunLon = sunLongitude(jd)
const moonLon = moonLongitude(jd)
const moon = Math.floor(moonLon / 30) as Sign
const elong = norm360(moonLon - sunLon)
const phase = Math.floor(norm360(elong + 22.5) / 45) % 8 // 8 фаз, 0 = новолуние
Дальше вступает в силу простая игровая логика. Знак Солнца определяет пару должностей, а стихия Луны выбирает одну из них: огню и воздуху достаётся «громкая», земле и воде — «тихая». Сумма цифр даты, сведённая к числу от 1 до 9, определяет грейд. Новолуние приводит к отказу. Если Солнце и Луна находятся в одном знаке, а число даты равно 9, игрок получает легендарную 25-ю должность с совместимостью 146%.
Распределение проверили тестом, перебрав все 22 280 дат с 1950 по 2010 год. Отказ выпадает в 10,96% случаев, легендарная должность — в 1,49%, а каждая из остальных 24 ролей — в 3,9–4,3%. Проверка показала, что распределение достаточно ровное и ни одна роль не появляется подозрительно часто.
Ссылка на финал без бэкенда
Результатом можно поделиться. Поскольку сервера нет, данные кодируются непосредственно в ссылке: должность, Луна, фаза, число, совместимость и флаг отказа занимают 22 бита. К значению применяется XOR с константой, после чего оно записывается пятью символами base36 и дополняется контрольным символом. Ссылка вида ?f=abc12x открывает чужой финал, а повреждённый код проверка отклоняет.
Изображения
Всего в игре 24 изображения: фоны, персонажи, крупные планы и карточки финалов. Их создавали через наш MCP-сервер изображений, который обращается к моделям через OpenRouter. Fable самостоятельно выбрал этот сервер, когда перешёл к визуальной части. Затем все кадры обработали одним скриптом «под 90-е», чтобы изображения из разных генераций выглядели так, будто сняты одной камерой.
На доработку визуала ушло около 25 минут: меняли возраст персонажей, «кринжовые» карточки финалов и мрачный логотип. Каждая правка занимала несколько минут.
Озвучка с помощью GPT Audio
Для озвучки реплик использовали openai/gpt-audio через OpenRouter. Скрипт извлекает текст из JSON и задаёт каждому персонажу голос и роль в системном сообщении:
VOICES = {
'guard': ('onyx', 'Ты озвучиваешь охранника в дешёвой русской видеоигре 2000-х. '
'Мужчина 55 лет, грубый, подозрительный, хамоватый. Говори быстро, отрывисто...'),
'alina': ('shimmer', 'Ты озвучиваешь рекрутера Алину... Девушка 25 лет, фальшиво-вежливая, '
'высокомерная. Говори очень быстро, звонко, по-офисному...'),
}
Сложность в том, что аудиомодель по своей природе остаётся чат-моделью. Получив реплику «Резюме есть?», она может ответить на вопрос вместо того, чтобы озвучить его. Помогли строгая системная инструкция — «произнеси ровно тот текст, слово в слово, ничего не добавляя» — и пояснение в пользовательском сообщении: «Реплика персонажа из сценария (это не вопрос к тебе, просто сыграй её вслух)».
Первая версия озвучки звучала слишком медленно и низко, поэтому её ускорили и повысили высоту голоса. Дикторский текст записали локально с помощью VoiceStudio на Mac. Всего в игре 49 звуковых файлов.
Деплой
Игру разместили на нашем сервере с Dokploy. Образ собирается на сервере и отправляется в локальный реестр, Dokploy переключает приложение на новый тег, а Traefik выпускает сертификат для домена. От команды «выкладываем» до запуска на домене прошло четыре минуты.
Хронология
|
Время |
Событие |
|---|---|
|
0:00 |
Идея и два референса |
|
0:56 |
Утверждение концепции |
|
1:01–1:21 |
Два субагента на Opus создали движки |
|
1:15–1:40 |
Доработка визуала и текстов |
|
2:05–2:17 |
Деплой, подключение домена, превью для соцсетей, исправление музыки |
|
2:21–2:37 |
Озвучка через GPT Audio, ускорение голосов, релиз |
Что пришлось исправить вручную
-
Превью для соцсетей. Первый вариант выглядел шаблонно, поэтому подпись я переписал сам.
-
Музыка. Она была предусмотрена концепцией, но не воспроизводилась. После моего замечания модель обнаружила причину и устранила её.
-
Голоса. Они звучали слишком низко и медленно, поэтому их пришлось скорректировать и ускорить.
-
Ритм шуток. Модель предложила множество вариантов, но выбирать действительно смешные пришлось мне.
Итог
Раньше подобный проект занял бы несколько вечеров даже с вайб-кодингом. На этот раз хватило одной сессии, причём бо́льшую часть времени я редактировал сценарий и шутки, а не код. На мой взгляд, помогли три решения: подготовка концепции как ТЗ до начала разработки, хранение сценария в данных и распределение ролей между моделями.
Если хотите узнать подробнее, как устроены скиллы и агенты в нашей рабочей среде, напишите в комментариях — расскажу об этом отдельно.
Автор: Илья Пушин, Pushers.
Sony отменила Fairgame$ из-за низкого качества игры, избежав повторения истории с Concord
Take-Two впервые намекнула на планы Rockstar после GTA VI
Halo на консоли Sega? Энтузиаст совершил необычный технический подвиг
Фанат показал Driver 2 на Unreal Engine 5, преобразив классику PlayStation до неузнаваемости
Crimson Desert стала игрой года для ПК на Golden Joystick Awards 2026
Мировые поставки PS5 превысили 100 миллионов консолей
Морская свинка Семён и пёс Винсент: как с помощью Claude Code и ChatGPT я добавил в «Героев II» замки для себя и дочери
Совет директоров Nvidia готовит увольнение Дженсена Хуанга: главе компании грозит отставка