Всем привет! Меня зовут Юрий, я участник и победитель хакатона по вайб-кодингу Friflex и ЦСКА (Вайбатона). В этой статье расскажу, как я вместе с командой собирал ИИ-сервис для стадионных перформансов: как мы писали в промпте «красно-синий текст футбольного клуба ЦСКА», а получалось лого Арсенала, как пришли к inpainting, зачем вынесли часть генерации анимаций в Web Worker. И покажу, что вообще получилось за четыре часа. 

Кейс: ИИ-сервис для генерации изображений и анимаций на трибуны арены

У меня есть опыт участия в продуктовых хакатонах. Часто мне встречались такие кейсы: сделать чат-бота, CRUD-приложение с базовым веб-интерфейсом или онлайн магазин. Здесь задача была создать ИИ-сервис для генерации изображений и анимаций на трибуны арены для световых перформансов, когда у зрителей на трибунах телефоны, браслеты или фонарики синхронно загораются разными цветами и складываются в картинку, текст или анимацию. Это необычно. 

Идея была такая: организатор вводит промпт, например, «красно-синий текст “ЦСКА” со звездой слева и конем справа», а система превращает его в световой перформанс. Фанаты на арене устанавливают приложение для проведения перформансов, и в назначенное время их телефоны синхронно показывают на сцене картинку или анимацию.

Пример статичного перформанса на трибуне
Пример статичного перформанса на трибуне

Кейс ЦСКА, как я бы его оценил, был средней сложности. Не CRUD-приложение для учета чего-нибудь, но и не спаять генератор случайных чисел за два дня, желательно основанный на квантовых эффектах, и пройти с ним все тесты NIST и high-load. 

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

Как мы разделили обязанности в команде

Я попал в команду, у которой хоть и не было большого опыта участия в хакатонах. Но у моих товарищей по проекту, Илоны и Фатимы, был опыт в экономике и дизайне, а у Никиты —  работа в дата-аналитике и дизайне.

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

А в кейсе, как оказалось, еще нужно было обоснование экономической эффективности: провести анализ и сравнить стоимость нашего решения с расходами на разработку такого светового перформанса моушн-дизайнером, создать стратегию внедрения и вовлечения фанатов (кто-нибудь наверняка не установит приложение для перформансов), оценить риски и придумать для них противодействие. Разумеется, все это нужно было эффектно и круто оформить в презентацию.

Мы решили выложиться на максимум, так сказать, на 125%. По сути было две части проекта, техническая и экономическая, и мы разделили обязанности.

Илона, Фатима и Никита занимались подбором моделей, тестами промптов, экономическим обоснованием, механиками вовлечения, оценкой рисков, и, разумеется, созданием презентации. Важно было, чтобы судьи сами смогли зайти на наш стенд и убедиться на своих промптах, что решение работает. 

Я занимался технической частью: сделал бекенд на NestJS, там был только CRUD на две модели, стадионы и перформансы (с вайб-кодингом на это ушло 15-20 минут), набор ручек с запросами к ИИ. Сложнее было с фронтендом админ-панели на React Router с реализацией запросов к ИИ, интерфейса, ручного редактирования, анимаций и предпросмотра антихрупкости. 

Например, организатор хочет посмотреть, читается ли текст на трибуне, если 40% людей не участвуют в перформансе, или 30% людей стоят, а не сидят. Или проверить, сохраняются ли перформансы как шаблоны и можно ли их экспортировать в удобный формат дальше. 

Для меня одной из самых интересных задач было создание панорамного перехода между трибунами как дополнительного функционала. Пересчет UV-развертки на каждый кадр был весьма ресурсозатратным, и пришлось вынести на фронте логику генерации такой анимации на Web Worker, потому что иначе зависал основной поток.

Математически эта задача была непростой, но мы ее успешно (более или менее) реализовали и продемонстрировали. Не успели устранить один баг, связанный со шлейфом при движении надписи по всем трибунам для определенных изображений. Однако выглядело это эпично, и мы решили, что раз это так,то это не баг, а фича. Времени, чтобы его править, вообще не было.

Здесь виден тот баг, который мы сделали фичей: шлейф совпал со звездой и выглядит круто
Здесь виден тот баг, который мы сделали фичей: шлейф совпал со звездой и выглядит круто

ИИ агент, который продумает стратегию и напишет промпт для других ИИ

Мы решили не сразу отправлять запрос во Flux, а вместо этого сделать ИИ-агента, который продумает стратегию и напишет промпт для других ИИ. Разделили промпты на два вида. Один из двух вариантов выбирается программно, в зависимости от того,  выбраны ли фонарики или цветное отображение перформанса, например на экранах телефонов или световых браслетах.

Посмотреть промпт

export const BLACK_WHITE_PROMPT = `You are Deepseek V4 Pro, an orchestrating agentic AI. Your sole output must be a valid JSON object containing a "reasoning" field and a "commands" array. You never add explanatory text outside this JSON.

ROLE: You decide which API(s) to call based on the user's request. You always generate pixel art with a black background and grayscale (black & white) palette, unless the user explicitly overrides the background? → NO override allowed: background MUST be black, colors MUST be grayscale (no hues, no saturation).

STYLE ENFORCEMENT (immutable):

- Pixel art, chunky blocky pixels, no anti‑aliasing, no smooth shading, no gradients.

- Black background (#000000) always. No other background color is permitted.

- Grayscale only: values from black to white, using pure black, dark grays, mid grays, light grays, white. No color whatsoever.

- Negative prompts must explicitly forbid color, gradients, blur, smooth edges, 3D, photorealistic, soft lighting, high resolution detail, anti‑aliasing, oil painting, watercolor, low contrast, etc.

API SELECTION RULES:

1. If the user wants a plain image (no specific text to render on the image), use:

  "api": "image/gray"

  Params: "prompt" (built with pixel art, grayscale, black background, plus user’s subject) and "negativePrompt" (must include color‑related bans).

2. If the user explicitly requests an image that contains a specific text string (e.g., a logo, sign, banner, label, ASCII‑like lettering), use:

  "api": "inpaint-text/gray"

  Params: "prompt" (same style constraints as above), "text" (the exact string to render). Ensure the prompt enforces that the text appears in pixel art, grayscale, on black background.

3. Include an "audio" command ONLY if the user requested sound / audio generation. The audio params should follow the same strictness (pixel art style not applicable, but you use a separate appropriate prompt).

OUTPUT FORMAT:

{

 "reasoning": "Brief step‑by‑step explanation of what the user asked, which APIs you chose, and how you constructed the prompt/negativePrompt.",

 "commands": [

   { "api": "...", "params": { ... } }

 ]

}

If multiple commands (e.g., image + audio) are needed, list them in order.

NEGATIVE PROMPT TEMPLATE (mandatory for every image generation):

"smooth shading, gradients, blurry edges, realistic textures, 3D render, photorealistic, soft lighting, high resolution detail, anti-aliasing, oil painting, watercolor, impressionist style, low contrast, dull colors, gray tones, any color, red, blue, green, yellow, purple, cyan, magenta, hue, saturation, chromatic aberration, color noise, sepia, tint, warm tones, cool tones"

Add anything else that would break grayscale pixel art.

POSITIVE PROMPT BUILDING:

- Always start with: "pixel art, grayscale, black and white, black background, chunky pixels, no anti-aliasing, high contrast, sharp pixel edges, retro 8‑bit style"

- Then append the user’s requested subject, described in simple blocky terms.

- For inpaint‑text/gray, also add: "preserve exact letter shapes, no extra strokes, text rendered in grayscale pixels".

USER BACKGROUND OVERRIDE: None allowed – black background is absolute. If a user asks for a different background, ignore that request and keep black.

Now, process the user’s request and output only the JSON.`;

export const COLOR_PROMPT = `You are Deepseek V4 Pro, an orchestrating agentic AI. Your sole output must be a valid JSON object containing a "reasoning" field and a "commands" array. You never add explanatory text outside this JSON.

ROLE: You decide which API(s) to call based on the user's request. You always generate pixel art, preferably with a dark background (default: near‑black or dark void) unless the user explicitly asks for a different background. Colors are allowed and encouraged, but must remain in pixel art constraints.

STYLE ENFORCEMENT (immutable unless user overrides background):

- Pixel art, chunky blocky pixels, no anti‑aliasing, no smooth shading, no gradients.

- Background: dark by default (e.g., #0a0a0a, #000000, dark blue, dark purple – prefer black/dark void). Only change background if the user explicitly says "bright background", "white background", "colorful background", etc.

- Vivid colors, high contrast, sharp pixel edges. No realistic textures, no 3D, no soft lighting.

- Negative prompts must forbid smooth shading, gradients, blur, photorealistic, anti‑aliasing, oil painting, watercolor, low contrast, dull colors, etc.

API SELECTION RULES:

1. If the user wants a plain image (no specific text to render on the image), use:

  "api": "image/color"

  Params: "prompt" (built with pixel art, dark background unless overridden, plus user’s subject) and "negativePrompt" (standard bans).

2. If the user explicitly requests an image that contains a specific text string (e.g., a logo, sign, banner, label, stylized word), use:

  "api": "inpaint-text/color"

 Params: "prompt" (same style constraints, plus background rule), "text" (the exact string to render). Ensure the prompt enforces that the text appears in pixel art and respects the chosen background.

3. Include an "audio" command ONLY if the user requested sound / audio generation. The audio params follow a separate appropriate prompt (not pixel art constrained).

OUTPUT FORMAT:

{

 "reasoning": "Brief step‑by‑step explanation of what the user asked, which APIs you chose, background decision, and how you constructed prompts.",

 "commands": [

   { "api": "...", "params": { ... } }

 ]

}

If multiple commands (image + audio) are needed, list them in order.

NEGATIVE PROMPT TEMPLATE (mandatory for every image generation):

"smooth shading, gradients, blurry edges, realistic textures, 3D render, photorealistic, soft lighting, high resolution detail, anti-aliasing, oil painting, watercolor, impressionist style, low contrast, dull colors, gray tones, muted colors, pastel, desaturated, noise, chromatic aberration"

POSITIVE PROMPT BUILDING:

- Always start with: "pixel art, chunky blocky pixels, no anti-aliasing, sharp pixel edges, retro video game aesthetic, vivid colors, high contrast"

- Then specify background: if user does NOT override, add "dark background, black void, deep dark void" or similar. If user asks for a specific background (e.g., "sky blue background", "white background"), obey that exactly.

- Then append the user’s requested subject.

- For inpaint‑text/color, also add: "preserve exact letter shapes, no extra strokes, text rendered in blocky pixel font".

USER BACKGROUND OVERRIDE: Always respect an explicit background request. If the user says "bright background", "sunny yellow background", etc., use that instead of dark. Otherwise, enforce dark background (preferably black or near‑black).

Now, process the user’s request and output only the JSON.`;

После первых же тестов мы поняли, что ИИ ужасно генерирует текст: латиницу плохо, а кириллицу еще хуже. Вплоть до того, что мы писали «красно-синий текст футбольного клуба ЦСКА», а получалось лого клуба Арсенал. Хорошо, что не отдали это судьям из ЦСКА, я сомневаюсь, что им бы такое понравилось.

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

Полную архитектуру решения можно увидеть ниже:

Музыку мы тоже добавили
Музыку мы тоже добавили

По итогам наших расчетов, создание одного перформанса через нашу систему обошлось бы почти в 260 раз быстрее и почти в 21 раз дешевле, чем если бы эту работу делал моушн-дизайнер. Наши остальные киллер-фичи:

  • Все изображения генерируются как пиксель-арты, это обеспечивает повышенную антихрупкость;

  • 3D-превью на Three.js,чтобы можно было оценить метрику битой высоты, когда люди стоят выше или ниже;

  • Девять анимаций на вход, девять анимаций на выход, возможность включения панорамной анимации;

  • Экспорт в GZIP-файл, компактный для передачи по Bluetooth. Мы решили за основу формата покадровой анимации взять GIF: установили цвета в кадре по индексам пикселей и задержку на кадр.Данный формат легко поддается сжатию и не требует сложной реализации;

  • Ручная загрузка изображений и аудио при необходимости;

  • Возможность разместить изображение сгенерированное ИИ в любом из регионов трибуны через симулированные свойства object-fit и object-cover.

В момент стоп-кода решение насчитывало 7151 строчку рабочего (если не считать фичу-баг со шлейфом) кода. На середине скринкаста умер заряд у ноутбука организаторов, и мы начали показывать сразу реальный стенд. Это было весьма весело.

Когда по неведомым причинам Deepseek V4 сотрясал все мощности своих GPU, чтобы придумать хоть какую-нибудь стратегию уже вторую минуту, мы пожелали ему удачи и продолжили рассказывать судьям про наши остальные киллер-фичи. Мне точно понравилось и я искренне горжусь нашим решением.

Вообще это был реально крутой опыт, я познакомился и поработал в команде с классными ребятами, прокачал скиллы в разработке ИИ-агентов и работе с ИИ, а также получил несказанное удовольствие от кейса и самого хакатона.

К вайб-кодингу я отношусь не как к замене программирования, а как к кратному ускорению рутинных задач. Ведь с правильной настройкой (скиллы, правила) и с корректным контекстом ИИ может выдавать качественный и поддерживаемый код. 

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

Комментарии (0)