Браузерный Kinect на коленке: пишем игру «Съедобное-несъедобное» с DeepSeek и TensorFlow.js

Браузерный Kinect на коленке: пишем игру «Съедобное-несъедобное» с DeepSeek и TensorFlow.js

Привет, VC.

Бывают выходные, когда инженерный зуд совпадает с родительским долгом. Задача стояла простая: развлечь двух свих детей 2 и 5 лет, потратить их энергию и, желательно, чему-то научить. Xbox с Kinect под рукой не оказалось.

И тут возникла мысль: у нас у всех в карманах лежат устройства с камерами и мощными процессорами. А в браузере уже давно живет TensorFlow.js. Почему бы не собрать аналог Fruit Ninja, но с образовательным уклоном, где контроллером будут руки ребенка?

В качестве эксперимента я решил не писать код руками. Вообще. Моим «джуном» на этот вечер стал DeepSeek. Мне было интересно проверить, на сколько он стал лучше с момента моего последнего эксперемента тут, способен ли он выдать рабочий продукт на стыке Computer Vision, Canvas-рендеринга и Web Speech API, или снова придется все переписывать самому.

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

Контекст: зачем нам TensorFlow в браузере?

Браузерный Kinect на коленке: пишем игру «Съедобное-несъедобное» с DeepSeek и TensorFlow.js

Немного истории. Раньше, чтобы распознать позу человека, нужен был C++, OpenCV, мощная видеокарта и пару дней на настройку окружения.

С появлением TensorFlow.js ситуация изменилась. Google портировал свои модели для запуска прямо в браузере, используя WebGL для ускорения вычислений на GPU клиента.

Для нашей задачи идеально подходит модель PoseNet (или более новая MoveNet). Она легкая, работает в реалтайме и выдает координаты ключевых точек тела: нос, глаза, уши, плечи, локти, кисти рук.Нам не нужно распознавать лицо или сложные жесты. Достаточно знать координаты leftWrist и rightWrist (левого и правого запястья), чтобы превратить их в курсоры на экране.

Задача для ИИ-ассистента формулировалась так:

  1. Захватить поток с веб-камеры.
  2. Наложить поверх скелет или маркеры рук (инференс модели).
  3. Отрисовать падающие объекты (физика на Canvas).
  4. Реализовать коллизии (рука коснулась объекта).
  5. Добавить геймификацию (счет, звуки).

Процесс разработки: Промт-инжиниринг вместо кодинга

Я использовал DeepSeek, так как он бесплатен и в последнее время показывает неплохие результаты в кодогенерации, иногда обходя GPT-4 в контексте понимания старых библиотек.

Весь процесс занял 9 основных итераций. Рассмотрим, какими именно командами я направлял «джуна».

Этап 1: MVP и базовая физика

Браузерный Kinect на коленке: пишем игру «Съедобное-несъедобное» с DeepSeek и TensorFlow.js

Начал с базы. Мне нужен был минимально рабочий прототип, соединяющий камеру и канвас. Важное уточнение в первом промте — ссылка на конкретное демо PoseNet, чтобы модель понимала, какой именно API я хочу использовать, а не выдумывала несуществующие библиотеки.

Промт 1 (Старт):

«Создай игру, где медленно двигаются небольшие разноцветные шарики сверху вниз. Пользователь при помощи рук сбивает эти шарики. Для отслеживания рук пользователя используй пример https://storage.googleapis.com/tfjs-models/demos/posenet/camera.html»

Результат: Рабочий прототип с первого раза. Шарики падали, камера работала, скелет рисовался.

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

Промты 2 и 3 (Допиливание UX):

«Сделай исправление при работе фронтальной камерой. Сейчас движение рук по горизонтали инвертировано. Убери инструкцию и изображение с камеры (оставь только канвас)».

«Добавь полноэкранный режим и выход из него. В полноэкранном режиме:

  1. Убери кнопки старт, продолжить, сброс.
  2. Убери лишний текст.
  3. Текст с отображением очков сделай небольшим и убери вниз экрана».

Решение от ИИ: Для инверсии был корректно применен transform: scaleX(-1) к видеопотоку и зеркалирование координат отрисовки на канвасе. Интерфейс был очищен и подготовлен к фуллскрину.

Этап 2: От шариков к фруктам и TTS

Браузерный Kinect на коленке: пишем игру «Съедобное-несъедобное» с DeepSeek и TensorFlow.js

Сбивать цветные круги скучно. Пора добавить контент и киллер-фичу — озвучку. Дети в 4–5 лет могут не знать названия всех экзотических фруктов, поэтому игра должна быть говорящей.

Промты 4 и 5 (Контент и звук):

«Поменяй шарики на эмодзи разных фруктов».

«При попадании во фрукт произноси его название на русском языке, используй TTS (Text-to-Speech)».

Инсайт: DeepSeek сам предложил структуру объекта Fruit с полями speed, rotation, score и emoji. Это позволило легко расширять игру в будущем.

С озвучкой есть нюанс: браузеры блокируют AudioContext до первого взаимодействия пользователя. ИИ корректно обработал это, инициализируя синтез речи по стартовому клику.

А вот с мобильными устройствами пришлось повозиться отдельно.Проблема: Экран гаснет, если не тыкать в него пальцем. А мы машем руками перед камерой.Решение: Wake Lock API.

Промт 6 (Мобильные костыли):

«В полноэкранном формате:

  1. Экран не должен уходить в спящий режим.
  2. Не показывать панель настройки звука».

DeepSeek сгенерировал код с использованием navigator.wakeLock.request('screen'), что критично для бесконтактных игр.

Этап 3: Игра в «Съедобное-несъедобное»

Браузерный Kinect на коленке: пишем игру «Съедобное-несъедобное» с DeepSeek и TensorFlow.js

Механика Fruit Ninja проста, но хотелось образовательного элемента. Я решил переделать игру в классическое «Съедобное-несъедобное».

Промт 7 (Смена логики):

«Преврати игру в игру 'Съедобное-несъедобное'. Теперь вместо фруктов будут появляться как съедобные, так и несъедобные предметы. Если игрок сбивает съедобное, он получает положительные очки, если несъедобное — отрицательные. Добавь озвучку, которая говорит название предмета и добавляет слово 'бяка' для несъедобного».

Логика изменилась корректно: съедобное плюсует счет, несъедобное минусует и добавляет забавный звуковой комментарий (например, «Носок — бяка!»).

Самое сложное здесь — датасет. Я попросил сгенерировать массивы предметов. И тут пришлось включить «родительский контроль». ИИ изначально, не подумав, добавил в несъедобное опасные предметы (ножи, иголки). Демонстрировать такое дошкольникам не стоит.

Промты 8 и 9 (Генерация и фильтрация данных):

«Добавь по 20 элементов каждого вида».

«Добавь по 40 элементов каждого вида. В съедобное добавь больше фруктов и полезного. Из несъедобного убери опасные предметы для детей до 5 лет. Оставь только безопасные бытовые вещи».

В итоге мы получили сбалансированный массив из 80 безопасных предметов.

Что «под капотом»?

Браузерный Kinect на коленке: пишем игру «Съедобное-несъедобное» с DeepSeek и TensorFlow.js

В итоге получился один HTML-файл (с включенным JS), который делает следующее:

  1. Инициализация PoseNet: Загружает модель MobileNetV1 (оптимизирована для слабых устройств).
  2. Game Loop (requestAnimationFrame):
  • Очистка Canvas.
  • Получение позы: net.estimateSinglePose(video) — самый ресурсоемкий шаг.
  • Фильтрация: берем только rightWrist и leftWrist, если уверенность модели (confidence) > 0.5.
  • Отрисовка и физика: Проход по массиву падающих объектов, обновление их Y-координаты.
  • Коллизия: Простая проверка дистанции между координатой руки и центром объекта.
  1. State Management:
  • Если коллизия с «съедобным» -> score += value, speak(name).
  • Если коллизия с «несъедобным» -> score -= value, speak(name + " бяка").

Пример логики генерации предметов (псевдокод на основе генерации ИИ)

javascript // То, что сгенерировал ИИ после 9-й итерации (упрощено) const edibleItems = [ { name: "Яблоко", emoji: "🍎", score: 10 }, { name: "Брокколи", emoji: "🥦", score: 15 }, // ... еще 38 позиций ]; const inedibleItems = [ // Опасные предметы были удалены по промту №9 { name: "Носок", emoji: "🧦", score: -10 }, { name: "Мыло", emoji: "🧼", score: -15 }, // ... еще 38 позиций ]; function spawnItem() { // Шанс 50/50, но можно подкрутить баланс const list = Math.random() > 0.5 ? edibleItems : inedibleItems; const item = list[Math.floor(Math.random() * list.length)]; gameObjects.push({ ...item, x: Math.random() * (canvas.width - 50), y: -50, speed: 2 + Math.random() * 3 // Рандомизация скорости }); }

Ошибки и подводные камни

Даже с помощью ИИ всё не так гладко, как в презентациях.

  1. Освещение — это всё. PoseNet — это компьютерное зрение. В полумраке модель начинает терять руки или «находить» их на фоне узора ковра. Играть нужно в хорошо освещенной комнате.
  2. Производительность. На десктопе (MacBook M1) всё летает. На бюджетном Android-смартфоне FPS может проседать, так как инференс модели каждый кадр — это дорого. Решение (не реализовано в MVP): Делать детекцию не каждый кадр, а каждый второй-третий, интерполируя координаты между ними.
  3. TTS и русский язык. window.speechSynthesis зависит от голосов, установленных в ОС. На мобильных всё обычно хорошо, а вот на десктопном Windows может подхватиться дефолтный «робо-голос», который пугает детей.

Выводы

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

Что мы получили:

  1. Образовательная ценность: 80 предметов, тренировка моторики и словарного запаса.
  2. Технологичность: Zero-install, работает по ссылке в браузере.
  3. Стоимость разработки: 0 рублей, пара часов времени на промтинг и тесты.

Насколько хорош код от DeepSeek? Он рабочий. Это не Enterprise-архитектура, всё свалено в один файл (что нормально для такого прототипа). Он удивительно хорошо справился с контекстом — помнил, что мы делали 5 шагов назад, и корректно вносил правки, например, расширяя массивы данных без поломки логики рендера.

Кому подойдет этот подход?

  • Продакт-менеджерам: Быстро проверить гипотезу (PoC).
  • Родителям-айтишникам: Быстро сделать кастомную развлекаловку.
  • Педагогам/методистам: Создавать простые интерактивные пособия без бюджета.

Кому НЕ подойдет?

  • Разработчикам высоконагруженных игр: JS + Canvas + PoseNet в таком виде — это не про 144 FPS и сложную физику.
  • Тем, кто ищет production-ready решение: Тут нет тестов, нет обработки всех edge-кейсов (например, если у пользователя нет камеры) и нет CI/CD.

Ссылка на репозиторий с результатом (файл 11allTS.html): GitHub FruitKinect

P.S. Ребенок доволен, соседи, возможно, не очень (хлопки и радостные крики ). Но цель достигнута.

Начать дискуссию