# fly-in-the-bottle Виджет для рабочего стола: анатомическая 3D-муха в стеклянной банке, которую можно тыкать, вращать и трясти. Electron + Three.js, прозрачное окно без рамки, которое может «жить на обоях» (Linux/X11 проверено; Windows поддерживается, но **на Windows виджет не запускался**). Поведением мухи управляет **настоящая коннектома самца дрозофилы (MaleCNS, 164 606 нейронов)**, которая считается в отдельном процессе. ## Что входит - прозрачное окно без рамки; слои «на обоях» (под всеми окнами), «обычное» и «поверх всех»; Linux и Windows - масштаб всего виджета колесом мыши (50-300%), запоминается - стеклянная банка с преломлением и хроматической аберрацией, рифлёная крышка, тень (без этикетки) - анатомическая дрозофила [FlyBody](https://github.com/TuragaLab/flybody) (67 тел, 85 мешей, 272 тыс. треугольников): двухзвенное IK ног, шаги «треногой», ход крыла - мозг: симулятор коннектомы MaleCNS на C++ (`brain/`), событийный, ~0 нагрузки в покое; он решает, когда муха летит, поворачивает, ходит (нисходящие нейроны DNp09, MDN, DNa01, DNa02) и что делает после тычка или встряски - запасная сценарная муха (полёт, посадка, чистка лапок, испуг, кувырок), если мозга нет - синтез звука без аудиофайлов: жужжание, «тинь» стекла, «тик» о стекло - тестовый стенд `--shot`: кадры и проверки без показа окна на экране - документация на Sphinx в `docs/` (обзор, quickstart, Windows, архитектура, модель мухи, мозг, справочник API) ## Quickstart 1. Поставьте зависимости приложения (нужны Node.js и npm): ```bash npm install ``` 2. Запустите виджет: ```bash npm start ``` Окно прозрачное, стоит в правом нижнем углу основного экрана и по умолчанию лежит **под всеми окнами**. Если рабочий стол закрыт окнами, банки не видно: нажмите `Super+D` или выберите в меню (правая кнопка) «Положение → Обычное окно» либо «Поверх всех окон». Колесо мыши над виджетом меняет его размер. `npm start` - это `node tools/dev.js start`: на Linux он добавляет `--no-sandbox` (на Ubuntu 24.04 непривилегированные user namespaces ограничены AppArmor, а `chrome-sandbox` из npm-пакета не setuid; без флага окно остаётся пустым и без ошибок) и убирает `ELECTRON_RUN_AS_NODE`, который выставляют терминалы VS Code. 3. (Один раз, по желанию) подключите мозг: ```bash npm run brain:setup # скачает коннектому (~1,1 ГБ), соберёт граф и симулятор npm run brain:check # проверка симулятора npm start ``` Понадобятся Python 3 и компилятор C++17 (`g++`, `clang++`, MSVC; если ничего нет, `zig` ставится сам из PyPI, около 80 МБ). `make`, `curl` и `bash` не нужны. Сборка графа занимает ~14 секунд и пиково около **2 ГБ RAM**. Без этого шага виджет работает на запасной сценарной мухе (`npm run start:scripted` включает её принудительно). ### Windows Те же три шага в PowerShell или `cmd`. Код для Windows написан (слой «на обоях» через окно-инструмент и `SetWindowPos(HWND_BOTTOM)`, `brain.exe`, сборка без `bash`), но **проверен только сборкой**: кросс-компиляция сайдкара под Windows проходит, а сам виджет и `brain.exe` на Windows не запускались. Если «на обоях» ведёт себя странно, выберите слой «Обычное окно» (или запустите `npm start -- --layer=normal`). Что проверено и что нет: [docs/windows.md](docs/windows.md). Полный разбор установки и типичные проблемы: [docs/quickstart.md](docs/quickstart.md). ## Управление | Действие | Что происходит | | --- | --- | | Клик по мухе | «тычок»: муху отбрасывает, а касание попадает в нейроны той части тела, куда ткнули | | Клик по стеклу | стук: банка вздрагивает, вибрация доходит до усиков и лап | | Быстро подвести курсор к мухе | для мозга это приближающийся объект с той стороны, откуда идёт курсор: муха отпрыгивает | | Вести курсор мимо мухи | для мозга это малый движущийся объект: муха идёт и поворачивает в его сторону | | Перетаскивание по стеклу | вращает банку (есть инерция), резкий рывок встряхивает муху | | Перетаскивание за крышку, либо Shift/Alt/Ctrl + перетаскивание | двигает виджет по столу (банка при этом раскачивается) | | Колесо мыши | масштаб виджета (щелчок = 10%, от 50% до 300%); окно растёт вокруг середины нижней кромки, банка стоит на месте | | Правая кнопка | меню: звук, мозг, размер (пресеты и сброс масштаба), положение, выход | ## Мозг Данные: [MaleCNS v1.0](https://male-cns.janelia.org/) (Janelia FlyEM и партнёры, CC-BY): мозг и брюшной нервный узел одной мухи. Берутся прослеженные нейроны (164 606) и связи от 5 синапсов (6,2 млн из 25,6 млн), знак связи - по предсказанному нейромедиатору (ацетилхолин +; ГАМК, глутамат, гистамин −). Модель - LIF по схеме [Shiu и др., Nature 2024](https://www.nature.com/articles/s41586-024-07763-9) (`brain/`, C++): порог 7 мВ, τ_m = 20 мс, τ_syn = 5 мс, задержка 2 мс, шаг 1 мс. Событийная: спящие нейроны ничего не стоят. ``` тело ──касание, вибрация, приближение, объект, ветер, вращение, нагрузка на лапы──▶ ток на сенсорные группы ──▶ МОЗГ тело ◀──мощность лётных мышц, прыжок, рулевые мышцы, ходьба, поворот и груминг (DN), мышцы ног/шеи/брюшка── частоты пулов ◀─┘ ``` Соответствия описаны в `tools/build_groups.py` (134 группы: сенсоры, моторные пулы, нисходящие нейроны). В коде тела (`src/brainfly.js`) нет сценария «летать», «садиться», «ходить», «падать», «чиститься» или «вставать»: только физика (крылья и прыжок толкают тело от стекла, вес и хватка лапок его держат, тело в воздухе свободно вращается, и выпрямить его может только взмах крыльев) и коэффициенты. Порогов «с какой мощности взлетать» и «с какой садиться» нет: и то и другое - баланс сил. Ритм даёт тело: в LIF-сети нет ритмогенератора, поэтому сеть даёт команды на ходьбу, поворот и груминг, а ноги переносятся и трутся друг о друга в своём темпе. Если мозг был установлен раньше, обновите группы и симулятор (появились группы `lc9`, `lc10d`, `mn_ttm`, `dn_groom` и другие, изменилась адаптация): `node tools/dev.js py tools/build_groups.py` и `npm run brain:compile`. Без новых групп «объект» сеть не видит, ходьба вперёд не запускается, прыжка (а значит и взлёта с пола) нет, груминга нет, а ноги не сообщают сети своё состояние (нет групп `cs_leg_*`, `co_leg_*`, `hp_leg_*`, `lg_leg_*`). **Что откалибровано, а что предположено** (подробности и таблицы: [docs/brain.md](docs/brain.md), [docs/brain_model.md](docs/brain_model.md)): - усиление синапса **0,15 мВ, а не 0,275** как у Shiu и др.: при 0,275 любой сильный вход зажигает всю сеть одним паттерном; - **адаптация порога** (+0,2 мВ за спайк, τ = 3 с): без неё в коннектоме есть самоподдерживающиеся петли (восходящие → нисходящие → интернейроны VNC → восходящие), и «полёт» не прекращается никогда. Прежние 0,1 мВ и 1,5 с гасили лётные мышцы, но остальная сеть после одного касания ноги тлела минутами (мышцы шеи, ног и брюшка подёргивались, а муха каждые несколько секунд взлетала); с 0,2 мВ и 3 с сеть после касания затихает ([docs/brain_model.md](docs/brain_model.md), п. 2); - исходящие связи мотонейронов отрезаны (их выход - мышца); - знак руления - соглашение: избыток активности левых рулевых мышц поворачивает муху налево; приближение справа сильнее возбуждает левые рулевые мышцы, поэтому муха уходит от угрозы; уровни фонового входа подобраны эмпирически; - постоянный фоновый шум **не работает**: до 30 Гц сеть молчит, с 40 Гц переходит в устойчивое возбуждение, промежуточного режима нет, поэтому шум убран. Вместо него сидящая муха раз в 5-15 с получает **редкое случайное воздействие** (касание, порыв ветра, дрожь стола, тень малого объекта); что оно вызовет, решает сеть, чей отклик близок к «всё или ничего» (доли исходов: [docs/brain_model.md](docs/brain_model.md)); - ходьба: DNp09 (вперёд), MDN (назад), DNa02 и DNa01 (поворот) читаются, но будят их только малый движущийся объект (LC9, LC10d; его создают курсор, который ведут мимо мухи, и случайные события) и касание головы; слева сеть отвечает на LC9 менее чисто, чем справа (свойство данных, причину не искали); роли этих нейронов взяты из литературы по памяти и не сверялись; - взлёт и посадка: порогов по мощности нет, они - баланс сил (крылья и прыжок TTMn против веса и хватки лапок); хватка и сила прыжка подобраны вручную, с пола муху поднимает только прыжок; - крылья: у каждого крыла свои размах и раскрытие (лётные мышцы и моторные нейроны крыла своей стороны), а муха поворачивает к более слабому крылу; в рулевом сигнале вычтен левый перекос рулевых мышц сети, а знак стороны угрозы несут моторные нейроны гальтеров; гальтеры бьют против крыльев. Частота взмаха (11,3 Гц) телесная; - ноги сообщают сети своё состояние (нагрузку, положение, скорость, концы диапазона, касание); сеть на них почти не отвечает, а ритмогенератора шага в ней нет ни в открытом, ни в замкнутом контуре, поэтому шаги остаются телесными; - груминг: нисходящие DNg62 и DNge078 (в аннотациях это aDN1 и aDN2, Hampel 2015) молчат в покое и отвечают на щетинки головы обеих сторон; сеть отвечает всплесками по полсекунды, поэтому есть «пыль», которую счищают несколько всплесков подряд; темп натирания задаёт тело. **Чего нет:** ритма шагов от сети (его даёт тело), зрения кроме детекторов приближения, малого объекта и фонового света, биомеханики полёта. Это **не** «муха, которая сама летает по коннектоме» (такой готовой не существует), а тело, чьи решения принимает настоящая нейросеть. GPU-бэкенд (CUDA) не делался: CPU-версия дешёвая (в покое ~0, всплеск до ~0,5 ядра, в среднем ~27% ядра при активном использовании) и не мешает играм; интерфейс `Backend` в `brain/brain.h` рассчитан на второй бэкенд. ## Модель мухи Тело - **FlyBody** (TuragaLab/flybody, Apache-2.0; Vaxenburg и др., Nature 2025): 67 тел, 85 мешей, 272 550 треугольников. Раньше стояла упрощённая NeuroMechFly v2 (39 мешей, 66 574 треугольника, не более 2000 на деталь); в FlyBody в 4,1 раза больше треугольников и видны мелкие детали: фасеточные глаза, глазки, жилки и мембрана крыла, сегменты брюшка с нижними пластинами, хоботок с щетинками, лапки с коготками. Размеры правдоподобны: 2,97 мм от головы до кончика брюшка, крыло 2,67 мм (измерено по мешам, с литературой не сверялось). Чего это не значит: - FlyBody - **самка**, а коннектома MaleCNS - **самец**: тело и мозг принадлежат разным мухам, и половых различий (размер, кончик брюшка, гребешки на передних лапках) в модели нет; - по препринту статьи FlyBody собрана из 67 вручную сегментированных компонентов, снятых конфокальной микроскопией, а не из микро-КТ целой мухи (саму статью в Nature прочитать не удалось); - сравнивались только две модели (NeuroMechFly v2 и FlyBody); с другими сканами и моделями (например, FlyWire или Virtual Fly Brain) сравнения не было, так что называть FlyBody «максимально близкой из возможных» нельзя: это самая детальная из двух проверенных. Подробно (сравнение, материалы, кинематика): [docs/fly_model.md](docs/fly_model.md). ## Документация Sphinx-сайт на русском лежит в `docs/`: обзор, quickstart, управление, режимы запуска, Windows, архитектура, стекло, модель мухи, мозг (данные, модель и калибровка, справочник групп и протокола), разработка, справочник API. ```bash npm run docs # создаёт .venv и ставит зависимости, если их нет; всегда полная пересборка ``` Готовый сайт: `docs/_build/html/index.html`. Без npm: `make -C docs html` (нужны `.venv` и `pip install -r docs/requirements.txt`). Сборка всегда полная (`clean` + `sphinx-build -E`), чтобы боковое меню не расходилось с деревом страниц; каталоги `docs/_build/` и `docs/api/generated/` в git не хранятся. ## Проверки Автоматических тестов на JS нет: виджет проверяется тестовым стендом `--shot` (кадры, поведение мухи, долгие прогоны), а мозг - скриптом. ```bash npm run brain:check # 23 проверки симулятора: тишина в покое, взлёт и прыжок на угрозу, # касание брюшка, градуированность, ходьба и поворот на объект, груминг, # ноги в контуре, крылья и гальтеры по сторонам .venv/bin/python tools/bench_brain.py # стоимость счёта и сверка серии с эталоном npm run shot # кадр в shots/shot.png (окно уходит за экран) ``` Примеры скриптов для стенда и подводные камни: [docs/development.md](docs/development.md). ## Режимы запуска | Команда | Что делает | | --- | --- | | `npm start` | обычный запуск; мозг стартует, если установлен и включён | | `npm run start:scripted` | запасная сценарная муха (`--no-brain`) | | `npm run shot` | режим скриншота: PNG с альфа-каналом без показа окна | | `npm run brain:setup` | скачать коннектому и собрать мозг | | `npm run brain:compile` | только пересобрать симулятор | | `npm run brain:check` | проверить мозг | | `npm run docs` | собрать документацию | Все скрипты идут через `tools/dev.js` (Node): он работает одинаково на Linux и Windows. Флаги (после `--`): `--no-brain`, `--layer=desktop\|normal\|top` (на один запуск), `--dev` (объект `window.__w`), `--shot[=файл]`, `--script=файл.js`, `--delay=мс`; если флаг повторён, действует последний. Не используйте `--debug`: его занимает Node. Настройки (положение, `scale`, слой, звук, мозг) лежат в `~/.config/fly-in-the-bottle/settings.json`. Подробнее: [docs/launch_modes.md](docs/launch_modes.md). ## Архитектура Три процесса: главный процесс Electron (`main.js`, `brain-host.js`), рендерер (`src/*.js`: сцена Three.js, ввод, звук) и сайдкар мозга (`brain/`, C++), с которым главный процесс говорит построчным текстовым протоколом. Всё, что зависит от системы (слой «на обоях»), лежит в `platform.js`. Стекло: муху рисуют в отдельную текстуру с альфой, а шейдер стенки банки берёт её с преломлением и добавляет отражения процедурной «студии». Частота кадров адаптивна: 60 при движении, 20 в покое. Схемы, IPC и главный цикл: [docs/architecture.md](docs/architecture.md), [docs/rendering.md](docs/rendering.md). ## Структура проекта ```text main.js # Electron: окно, меню, настройки, масштаб, IPC, режим скриншота platform.js # слой окна «на обоях»: Linux (тип desktop) и Windows (SetWindowPos через koffi) preload.js # мост window.widget между рендерером и главным процессом brain-host.js # процесс-хозяин сайдкара мозга и его протокол src/ index.html # страница, import map, CSP app.js # сцена, мышь, колесо, главный цикл кадра jar.js # банка, шейдер стекла, крышка, тень env.js # процедурная «студия»: отражения (GLSL + PMREM) flymodel.js # тело мухи: меши, скелет, IK ног fly.js # сценарная муха и общая механика (столкновения, поза, звук) brainfly.js # муха с мозгом brainlink.js # связь рендерера с сайдкаром motion.js # физика банки как целого audio.js # синтез звука assets/fly/flybody/ # rig.json + meshes.bin (FlyBody, Apache-2.0), лицензия, NOTICE.md brain/ # C++ сайдкар: brain.h, cpu.cpp, main.cpp, Makefile tools/ dev.js # запуск, загрузка данных, сборка мозга, документация (Linux/Windows) build_brain_graph.py # граф и таблица нейронов build_groups.py # именованные группы нейронов build_fly_flybody.py # rig.json и meshes.bin из клона FlyBody probe_flybody.py # контрольные проекции позы покоя FlyBody brain_client.py # Python-клиент сайдкара check_brain.py # проверки мозга bench_brain.py # стоимость счёта explore/ # эксперименты калибровки docs/ # Sphinx-документация data/ shots/ .venv/ # не хранятся в git: данные и граф, кадры стенда, окружение Python ``` ## Заметки - Положение окна по умолчанию - «на обоях»: на Linux тип окна X11 `desktop` (всегда под остальными, на всех рабочих столах, вне панели задач и Alt-Tab), на Windows окно-инструмент, прижатое ко дну порядка окон. Кликабельность в этом слое на Linux на проверенной машине не проверялась (экран был целиком закрыт окнами), на Windows слой не запускался вовсе; при проблемах выберите слой «Обычное окно». - Не отправляйте синтетический ввод (`xdotool` и подобное) на настоящий рабочий стол для проверки виджета: проверяйте события `PointerEvent`, отправленные внутри страницы (см. [docs/development.md](docs/development.md)). - Данные: коннектома MaleCNS - Janelia FlyEM, Кембридж, Google (CC-BY); меши и скелет тела - FlyBody (TuragaLab/flybody, Apache-2.0, лицензия в `assets/fly/flybody/LICENSE-flybody.txt`, описание правок в `NOTICE.md`); модель нейрона - Shiu и др. 2024.