# Муха смотрит телевизор Художественная вычислительная работа, сентябрь 2026. Проект завершён и больше не поддерживается, код выложен как есть. Кухня 90-х, старый телевизор с живым эфиром российских каналов, перед ним муха. Её мозг — симуляция 166 700 нейронов по реальному коннектому дрозофилы (MaleCNS v1.0, Janelia FlyEM). Муха «смотрит» эфир 2 модельные секунды и либо остаётся, либо летит к пульту и переключает канал. Рядом копится статистика по каналам, светится облако нейронов по настоящим спайкам и показан «глаз мухи» — то, что реально получают фоторецепторы. Смысла передач муха не понимает: сеть получает только пиксели. «Скучно» и «испугалась» — авторская трактовка активности клеток Кеньона и giant fiber. ## Как было сделано ``` IPTV-плейлист iptv-org (РФ) ─► ffmpeg: текущий и следующий канал, 160×90 @ 15 fps ─► 4 146 фоторецепторов (кадр → колонки зрительной доли по коннектому) ─► LIF-симуляция 166 700 нейронов / 6,24 млн связей, шаг 0,1 мс (C++-ядро из Stonkfly) ─► окно 2 модельные секунды: giant fiber → «испуг», клетки Кеньона → «интерес / скука» ─► остаться или переключить ─► FastAPI + WebSocket ─► браузер (three.js: комната, 3D-муха, пульт, облако нейронов) ``` - **Бэкенд:** Python 3.12, FastAPI/uvicorn, один процесс = одна муха на всех зрителей. - **Фронт:** three.js, процедурная муха, пульт, облако из 139 662 нейронов с известными координатами. - **Каналы:** ~170 рабочих потоков из iptv-org, собирались своим скриптом с проверкой потоков. - **Сделано** в формате вайбкодинга с Claude Code. ![Муха смотрит телевизор](docs/design/screens/desktop-watching.png) ## Итоги (17–22 сентября 2026) За 5 дней в эфире муха приняла 17 395 решений на 118 каналах: - в 76% случаев осталась смотреть (13 158 раз); - 4 237 раз переключила: 4 203 раза от «скуки» и только 34 раза от «испуга»; - дольше всего задерживалась на «Патриоте», КВН ТВ и «ТВ21». ## Устройство подробнее ``` channels.json (iptv-org) ─► ChannelBank (открыты текущий и следующий канал, ffmpeg 160×90@15 + превью 640×360) ─► vision/encode.py (контраст и осветление кадра) ─► sim/retinal_network.py (4 146 фоторецепторов, LIF-ядро Stonkfly, шаг 0,1 мс) ─► sim/remote.py (окно 2 модельные секунды → остаться / переключить) ─► server/runtime.py → WebSocket (JSON-тик + бинарные кадры: спайки, ТВ, «глаз мухи») ─► static/ (three.js: комната, муха, пульт, облако нейронов) ``` ## Запуск Нужны Python 3.12 (на Apple Silicon — ARM-сборка), FFmpeg и компилятор C++17. ```bash python3 -m venv .venv-retinal .venv-retinal/bin/pip install -r requirements-retinal.lock -e . .venv-retinal/bin/python -m uvicorn fly_news.server.app:app --app-dir src --host 127.0.0.1 --port 8001 ``` Откройте http://127.0.0.1:8001. При первом старте ядро симуляции само соберётся из `third_party/stonkfly/kernel.cpp` в `artifacts/`. **Данные, которых нет в git:** - `data/connectome/male_cns_v1_0_full/` (159 МБ): собирается `scripts/build_full_connectome.py` из официального bulk-экспорта MaleCNS (скрипт скачивает ~1,1 ГБ; нужны `pandas` и `pyarrow`). - Оптическая карта `artifacts/retina-v1.npz` уже в репозитории. Статистика пульта копится в `state/remote-stats.json` и переживает перезапуск. Чтобы начать заново, удалите файл при остановленном сервере. Сервер запускается строго одним процессом: несколько воркеров — это несколько разных мух. ## Каналы Источник каналов — **IPTV-плейлист iptv-org по России**: `https://iptv-org.github.io/iptv/countries/ru.m3u` (все страны — `https://iptv-org.github.io/iptv/index.m3u`). Допустимость его использования проверил владелец проекта. ```bash .venv-retinal/bin/python scripts/update_channels.py # пересобрать channels.json (рабочие потоки до 720p) .venv-retinal/bin/python scripts/update_channels.py --ffmpeg # строже: ещё и декодировать кадр .venv-retinal/bin/python scripts/update_channels.py --steady 20 --workers 8 # ещё строже: 20 с живого приёма, рвущиеся потоки отсеиваются ``` Скрипт отбрасывает копии со сдвигом времени, «Not 24/7», дубли и нерабочие ссылки, сохраняет нужные потоку `Referer`/`User-Agent`. Каналы с `"pinned": true` (официальные потоки smotrim: Россия 24, Россия 1, Культура) остаются первыми. Выключить канал — `"disabled": true`. Доступность зависит от региона: на новом сервере список нужно пересобрать. Одновременно открыты только текущий и следующий канал, поэтому число каналов не влияет на нагрузку. Канал без кадров в сумме 8 секунд (провалы до 3 секунд муха пережидает на застывшем кадре) или без решения мухи 90 секунд пульт пролистывает сам. После перезапуска муха продолжает с того же канала. ## Проверки ```bash PYTHONPATH=src .venv-retinal/bin/python -m pytest -q tests .venv-retinal/bin/python scripts/observe_server.py --port 8001 --seconds 120 # трафик, скорость, задержка переключения PYTHONPATH=src .venv-retinal/bin/python scripts/probe_retinal.py # скорость шага сети на этой машине ``` ## Сервер Нужны 2–4 выделенных vCPU с сильным одним потоком (симуляция занимает одно ядро целиком, ffmpeg двух каналов ещё ~0,5), 2–4 ГБ RAM, GPU не нужен. Запускать строго одним процессом (`--workers 1`), за reverse proxy с поддержкой WebSocket. Требования и замеры — [docs/PROJECT_OVERVIEW.md](docs/PROJECT_OVERVIEW.md). ## Документы | Файл | Что внутри | |---|---| | [docs/PROJECT_OVERVIEW.md](docs/PROJECT_OVERVIEW.md) | концепция, что есть и чего нет, замеры, требования к серверу | | [docs/REMOTE_MODEL_NOTES.md](docs/REMOTE_MODEL_NOTES.md) | как решение читается из спайков, каналы | | [docs/RETINAL_MODEL_NOTES.md](docs/RETINAL_MODEL_NOTES.md) | глаз (RGB → фоторецепторы) и динамика сети | | [docs/FLY_NEWS_PROJECT_SPEC.md](docs/FLY_NEWS_PROJECT_SPEC.md) | исходный бриф и границы допущений | | `docs/reports/` | JSON-отчёты проверок | | `docs/design/screens/` | скриншоты | ## Благодарности Ядро симуляции и оптическая карта — из [nftechie/stonkfly](https://github.com/nftechie/stonkfly) (MIT). Процедурная муха — по мотивам doomfly (MIT). Коннектом — Janelia FlyEM MaleCNS v1.0. Каналы — [iptv-org](https://github.com/iptv-org/iptv). ## Лицензия MIT, см. [LICENSE](LICENSE). Код из `third_party/stonkfly` — под собственной лицензией MIT (`third_party/stonkfly/LICENSE`).