Сонар-управление + Asteroids. Игра на 50 КБ, которой нужен флагманский смартфон

Телефон лежит на столе. Сбоку от него в воздухе ладонь. Поднимаешь её — корабль на экране поднимается, опускаешь — опускается. Экрана не касаешься. Камера не нужна: телефон пищит ультразвуком и слушает эхо своим микрофоном.
Играть: sonaroids.app (лучше на iPhone). Код: github.com/neokrasav4ik/sonaroids, MIT.
С чего всё началось
Мне попался проект sonar.cool: Мак листает страницы жестами руки, а «видит» их через собственные динамики и микрофон. Мака у меня нет, зато есть iPhone. Отсюда вопрос: получится ли то же самое в браузере на телефоне? Причём не отдельный жест «листай», а непрерывное управление: высота ладони = высота корабля.
Сразу признаюсь: это вайбкодинг
Сам я не написал ни строчки. Обработку сигнала, игру, сервер рекордов, тесты и даже гифку написал Claude Opus 5.5. От меня были идея, решения «так / не так» и проверка каждой версии своей ладонью. Для разбора я записывал с телефона сырые WAV: ладонь по линейке на 5, 10, 15 см. Модель гоняла по этим записям свои варианты обработки, пока цифры не сходились с линейкой. Ещё она сама собрала себе стенды: синтетический микрофон, повтор записанных партий, игру в Chromium без окна.
Как это работает
Зонд. Периодический сигнал из 23 тонов от 18,4 до 20,4 кГц, период 512 отсчётов при 48 кГц. AudioWorklet отдаёт поток микрофона без пропусков, так что картина эха считается каждые 10,7 мс.
Движение ладони видно по сдвигу фазы эха. Это точно, до долей миллиметра, но со временем уплывает.
Положение видно по тому, как эхо отличается от пустой комнаты. Это шумно, зато не уплывает. Обе оценки сливаются: фаза ведёт корабль, положение не даёт ему уползти.
Калибровки как таковой нет. Убираешь руку на секунду (игра запоминает пустую комнату), машешь ладонью на 5–15 см над столом, и экран подгоняется под твой размах.
Что не сработало: пик дальности (прыгал на 6–22 см), амплитуда эха (сплошной шум от пятен интерференции), стандартный AnalyserNode (окна слишком грубые) и две руки (Safari даёт только один микрофон).
Почему «нужен флагман»
Ирония в том, что игра весит около 50 КБ в сжатом виде: графика, звуки и пиксельный шрифт нарисованы кодом. А запускается нормально только на хорошем железе. Всё решает полоса 18–20 кГц: динамик и микрофон телефона вовсе не обязаны её пропускать.
телефон | спад к 20,4 кГц | эхо ладони над пустой комнатой |
|---|---|---|
iPhone 16 | около −8 дБ | около 40 дБ |
OnePlus 13 | −33 дБ | 10–15 дБ |
Redmi Note 10 | около −20 дБ | 10–15 дБ |
Точнее даже так: нужен не просто флагман, а iPhone. OnePlus 13 тоже флагман, но играть на нём почти невозможно. Ещё у него проваливается вход: телефон теряет куски сигнала. Redmi слышит ладонь только со стороны фронтальной камеры, а не со стороны разъёма, где по логике стоят динамик и микрофон.
Часть проблем удалось обойти. Полоса выравнивается по уровню каждого тона: на записи с OnePlus ошибка против линейки упала с 81 до 13 мм. После провала входа прямой сигнал ищется заново. Игра сама выясняет, у какого конца телефона держать руку. Но 25 дБ разницы в эхе не вытянешь, поэтому Android пока «как повезёт с телефоном».
Рекорды без читеров
На сервер уходит не счёт, а вся партия: номер раскладки и высота ладони на каждом шаге. Это несколько килобайт на пару минут игры. Полёт детерминирован: постоянный шаг 60 Гц, генератор случайных чисел от номера раскладки, никаких sin и pow, которые могут по-разному округляться в разных движках. Поэтому сервер переигрывает партию тем же самым файлом правил, что и браузер, и записывает только счёт, который повторился. «Прислать 999999» не получится. Бот, который честно водит ладонью, пройдёт, но пусть сначала его кто-нибудь напишет.
Сервер — Node 22 без единого npm-пакета: node:http и встроенный node:sqlite. Ник — только латиница, цифры и _: ради защиты от желающих вписать в ник SQL-инъекцию.
Что дальше
Игра работает, рекорды пишутся: сегодня, неделя, всё время. Если у вас Android (особенно Pixel или Samsung), попробуйте и напишите в комментариях, как вышло: модель телефона, слышит ли он ладонь и с какого конца. Если игра не слышит ладонь, тап по номеру версии на экране проигрыша включает журнал, его можно приложить к issue на GitHub.
Ссылки:
игра: sonaroids.app
проект, с которого всё началось: sonar.cool
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.