У нас дома есть кладовка, забитая коробками, которые мы так и не разобрали после переезда. Пару недель назад оттуда начало время от времени пищать. Писк повторялся так редко, что стоять посреди комнаты и пытаться понять, откуда он, было мучением, а перерывать все коробки подряд мне очень не хотелось. Поэтому я попросил Claude Code написать Beep Locator: программу, которая находит источник загадочного писка по нескольким устройствам, расставленным по комнате.
Вот как Beep Locator выглядит после того, как поработал какое-то время:

Вы запускаете веб-сервер на Python, который показывает основной интерфейс, а потом по QR-коду или ссылке подключаете в роли слушателей столько устройств, сколько не жалко. У меня это были два iPad, телефон, MacBook Pro и Surface Ultra.
Затем устройства проигрывают друг другу звуки, чтобы оценить, где каждое из них стоит. Получалось это не очень, поэтому я добавил «грубый режим»: значки устройств можно перетащить на карте примерно туда, где они стоят, а система потом уточняет их положение по тем же звукам.
После настройки все устройства слушают, не раздастся ли писк.
Ещё я добавил режим хлопков, чтобы проверять систему: хлопаешь в ладоши в разных местах комнаты, и хлопки появляются на карте ромбиками. Это прибавило мне уверенности, что находить звук она умеет.
Писки отображаются на карте крестиком и строкой в журнале найденных звуков. Каждый можно переслушать (по очереди с каждого устройства), чтобы убедиться, что это тот самый писк, а потом нажать «Это загадочный писк» и пометить его.
Когда частота загадочного писка подтверждена, система следит именно за ней, измеряет интервал между писками и по вычисленным точкам сужает самое вероятное место на карте.
Всё сработало как задумано. Система указала положение писка с точностью примерно 40 см, я перерыл именно эту стопку коробок и нашёл древний датчик угарного газа, которому отчаянно требовались новые батарейки.
Как это работает
Когда я начал разбираться с задачей вместе с Claude, он быстро откопал статью 2007 года BeepBeep: A High-Accuracy Acoustic-Based System for Ranging and Localization Using COTS Devices (авторы Chunyi Peng, Guobin Shen, Yongguang Zhang, Yanlin Li и Kun Tan). Вот её аннотация:
Мы описываем проектирование, реализацию и оценку BeepBeep, высокоточной системы измерения расстояний по звуку. Она работает спонтанно, без заранее развёрнутой инфраструктуры, напрямую между устройствами. Это чисто программное решение, которому нужен лишь самый базовый набор обычного оборудования: динамик, микрофон и какой-нибудь канал связи между устройствами. Поэтому оно применимо к множеству недорогих сенсорных платформ и к большинству серийных мобильных устройств вроде сотовых телефонов и КПК. Высокая точность достигается сочетанием трёх приёмов: двусторонних измерений, записи собственного сигнала и подсчёта отсчётов.
Звучало ровно как то, что нам нужно было построить!
Основную работу в Beep Locator делает веб-приложение на Python. Слушающие устройства подключаются к нему по WebSocket и показывают интерфейс, который запрашивает у браузера доступ к микрофону. Главное, что делают слушатели, это держат в памяти буфер последних 60 секунд звука. Ещё они запускают простой детектор писков (см. detector.js): быстрое преобразование Фурье плюс фильтр высоких частот выделяют возможные писки, и о каждом таком событии слушатель сообщает серверу.
Когда приходит сообщение о писке, сервер просит одно из устройств (с наименьшим временем отклика до сервера) проиграть короткий чирп, а затем запрашивает у каждого слушателя фрагмент записи: примерно от 0,6 с до писка до 0,8 с после этого чирпа. По чирпу фрагменты выравниваются друг относительно друга, и это ключевой шаг в поиске писка.
Как я это собрал
Начал я с Claude Code Cloud на iPhone, с моделью Opus 5.5 в режиме повышенного усилия на размышления (Extra). Вот общий доступ к той стартовой сессии. Она началась с такого промпта в только что созданном пустом репозитории на GitHub:
Напиши файл problem.md с описанием задачи, которую мы хотим решить.
В комнате пищит какое-то устройство, и я не могу его найти.
Я хочу расставить по комнате несколько устройств (ноутбуки, телефоны, iPad), чтобы все они слушали одновременно и помогали триангулировать, откуда идёт писк.
Нужно веб-приложение на Python, которое всё координирует: одно устройство показывает страницу приложения, другие заходят по ссылке и становятся слушателями. Всё через WebAudio в браузере и WebSocket к серверу на Python.
В идеале я не хотел бы вручную вводить взаимное расположение устройств. Было бы потрясающе, если бы они проигрывали друг другу звуки и так определяли, где кто стоит на плоскости. Но возможно ли это вообще?
Так появился файл problem.md. Пока Claude его писал, он нашёл и прочитал статью про BeepBeep, и она там описана.
Исследование выглядело толково, поэтому я написал:
Хорошо, попробуй это собрать. Добавь механизм, через который можно отправлять слушателям новый код, а они будут его сразу запускать. Тогда я расставлю всё в комнате и оставлю тебя дальше хакать и экспериментировать, пока сам занимаюсь другими делами.
На этом этапе большая часть системы была написана, но запускать её ещё никто не пробовал.
Я склонировал новый репозиторий на ноутбук и запустил Claude Code… и тут вышла Fable 5.2. Проект показался отличным поводом проверить её в деле.
Я попросил Claude Code на ноутбуке запустить сервер и убедился, что он может и видеть происходящее, и менять программу прямо на ходу.
Это уже было заложено в систему: Opus 5.5 написал у слушателей метод installModule(), который вызывается через WebSocket. То есть сервер-администратор мог выполнить на любом клиенте произвольный новый код.
На некоторых устройствах страница, слушающая микрофон, заново спрашивает разрешение после каждой перезагрузки. Горячая подмена модулей позволяла выкатывать новый код, не обходя вручную каждое устройство.
Дальше моё участие в основном свелось к тому, что я сидел у ноутбука, обсуждал с Fable 5.2 происходящее и время от времени просил новые функции. Вот некоторые из этих промптов:
Думаю, надо сделать так, чтобы в «грубом режиме» можно было перетаскивать объекты по карте. Моя задача только поставить их примерно туда, где они стоят, а потом я выключаю грубый режим, и система пытается уточнить положение.
Так у меня появилась возможность перетаскивать слушателей по карте.
Интересно, как бы это проверить? Может, пусть слушает хлопки: я хлопну, а система определит, где был хлопок? […]
Хлопки должны обозначаться на карте другим значком, не таким, как писки.
Отсюда та самая функция: каждый мой хлопок появлялся на карте значком, отличным от писков.
У каждого значка на карте должна быть метка времени ЧЧ:ММ:СС.
Так стало проще понять, какой значок какому реальному звуку, скорее всего, соответствует.
В журнале должны быть писки и хлопки с метками времени И маленькая кнопка воспроизведения. По нажатию она по очереди проигрывает записанный звук с каждого слушателя (с понятной надписью на экране, какой слушатель записал какой фрагмент) и на время этого воспроизведения отключает запись с микрофона.
Теперь я мог переслушивать звуки, и найти среди записей тот самый загадочный писк стало гораздо проще.
Рядом с записью должна быть кнопка «Это загадочный писк»: я нажимаю воспроизведение, слушаю и подтверждаю, что это он, если я ищу именно его.
Тогда система сможет в дальнейшем классифицировать звуки, понимать, похожи ли они на тот же загадочный писк, и по этим данным точнее определять, где он.
К этому моменту я был уже близко: я неплохо представлял, откуда идёт писк. Но разве не здорово было бы переставить слушателей вокруг этой точки и уточнить место ещё сильнее?
Хочу для этого функцию. Не хочу перезапускать каждое слушающее устройство и так далее. Хочу нажать кнопку «Переставить устройства», которая переключает в режим перестановки, потом нажать «Переставлено», и система переводит меня в грубый режим и просит примерно указать, где они теперь. Я это делаю, нажимаю, и всё снова запускается. Предыдущую сессию можно посмотреть в приложении в режиме только для чтения.
А поскольку у меня оставалось одно свободное устройство, я добавил:
Добавь функцию: на карте отмечается место, куда было бы полезно поставить ещё одного слушателя, исходя из уже собранных данных.
Даём Claude Code уши
По-моему, самое интересное в этом проекте то, что он показал: Claude Code можно дать уши.
Своего аудиовхода у Claude нет (пока), но он может запускать код на JavaScript и Python, который анализирует звук с микрофона.
Я попросил его запустить веб-сервер с WebSocket-подключениями к нескольким слушающим устройствам, включить на них микрофон через WebAudio и связать всё так, чтобы через тот же WebSocket им можно было присылать свежий JavaScript на выполнение.
После этого Claude можно было практически оставить одного: он сам ковырял задачу, разбирал журналы звуков и перенастраивал свои уши, чтобы её решить.
Мне это напомнило похожий трюк, о котором я услышал на GitHub Universe несколько недель назад. Martin Woodward рассказал, как отлаживал компьютеры в бейджах GitHub Universe: подключал их к компьютеру по USB-C и направлял веб-камеру на их экран, чтобы кодинг-агент сам видел, сработало обновление или нет.
Придумывать, как дать кодинг-агентам воспринимать окружающий мир, очень весело.
