作品简介与玩法
《赛博果蝇》把 3,330 个真实神经元搬进浏览器实时运行。玩家不能直接操纵果蝇:你只能布置环境,果蝇的每一次转向和逃生都由仿真网络自己决定。画面里的物体变成对感觉神经元的刺激,下行神经元的放电再变成动作,这条链路在你眼前实时跑。
四种玩法:追逐——拖动金色目标,看果蝇经 LC10a → DNa 通路追过来,距离小于 65 场地单位时累计得分;逃生——点击投放苍蝇拍阴影,阴影放大 1.7 秒后落下,果蝇要靠巨纤维 DNp01 逃脱;混战——威胁与目标自动出现,玩家有 3 次神经刺激道具,存活 45 秒获胜,被拍 3 次结束;接线实验室——自由重接 6 个环境信号与 6 个感觉组,把输出改接到左转、右转或冲刺,看行为怎样改变。
鼠标或单指拖动操作,竞技场聚焦时可用方向键移动目标、空格投放威胁。竞技场是环形边界,果蝇穿过边缘会从另一侧出现。
依据:fly-arena/README.md、fly-arena/js/game.js。
制作工具与技术栈
- 数据:Janelia 公开的雄性完整中枢连接组(Berg 等,Cell 2026,CC-BY 4.0)。本作品从中提取了一个 3,330 神经元、186,406 条带符号连接的子网络。
- 仿真:Shiu 等(Nature 2024)的漏积分发放(LIF)模型参数,突触强度 0.1 mV 在完整雄性中枢上校准。
- 数据提取与验证:Claude Code 负责筛选输入输出神经元、提取子网络、用 Python + NumPy 离线验证行为。
- 游戏开发:Codex CLI 编写全部前端代码;Claude Code 试玩、实测性能并接入栏目。
- 游戏本体:原生 JavaScript ES modules + Canvas 2D,无构建步骤、无外部资源;Web Worker 跑神经网络,TypedArray 存膜电位与 CSR 邻接;Node.js 内置测试。
制作步骤
这是本作品真实的制作过程。
- 挑选输入与输出神经元。输入选了感知逼近的 LPLC2 / LC4(分左右)、感知移动小目标的 LC10a(分左右)、腿部 ppk23 信息素神经元和 PAM 多巴胺神经元;输出选了负责转向的 DNa01 / DNa02、负责逃生起跳的巨纤维 DNp01。
- 把 16.5 万个神经元裁到浏览器能跑的规模。只保留突触数 ≥3 的连接,再取同时位于输入下游与输出上游的神经元,得到 3,330 个神经元、186,406 条连接,导出 2.4 MB 的
subnet.json。 - 先在 Python 里验证子网络。150 Hz 刺激 1 秒的结果:左侧逼近 → 逃生 220 Hz 且向右转 20 Hz;右侧逼近 → 逃生 230 Hz;左侧目标 → 左转 50 Hz 且逃生为 0;右侧目标 → 右转 46 Hz。裁剪后的网络仍然保留了“威胁躲开、目标追上去”的行为。
- 把参考脚本交给 Codex 做游戏。要求浏览器里必须跑真正的 LIF,而不是预设数值,并把上述数值写成单元测试。
- 实测与修正。14 个测试全部通过;实测每批 50 ms 脑时间耗时 5.8 ms,稳定运行在 3.00× 脑时间速度、60 帧。启动瞬间的速度读数会因为首批间隔过长显示成 0.05×,改为跳过超过 200 ms 的间隔后,一启动就显示 3.00×。
# 本地运行
cd fly-arena && python3 -m http.server 8000
# 运行测试与基准
node --test tests/*.test.js
node tests/benchmark.mjs
关键实现要点
- 浏览器里跑的是真仿真。Web Worker 中每帧模拟 50 ms 脑时间,步长 0.1 ms:静息与复位 −52 mV、阈值 −45 mV、膜时间常数 20 ms、突触时间常数 5 ms、不应期 2.2 ms、突触延迟 1.8 ms,每个脉冲给突触后神经元增加 权重 × 0.1 mV。这与 Python 参考脚本逐步对齐。
- 出边转成 CSR 结构,配 18 槽脉冲环形队列实现 1.8 ms 延迟;只有放电的神经元才遍历出边,因此 18.6 万条连接不会每步全扫。
- 感觉映射:物体的角大小与接近速度决定刺激频率,落在左右两侧对应的输入组上,上限 150 Hz。动作映射:转向取左右转神经元的放电率之差,逃生放电超阈值触发冲刺。
- 前进速度是人为设定的常数,页面上明确标注,因为该子网络里负责前进的下行神经元基本不放电。
- 脉冲栅格画的是真实仿真事件,采样自 LPLC2、LC4、LC10a、DNa01/DNa02、DNp01 等真实神经元,不是装饰动画。
依据:fly-arena/js/lif.js、fly-arena/js/brain-worker.js、fly-arena/docs/verification.md。
局限说明
- 这不是数字果蝇。连接组是真实的,但神经动力学被大幅简化:所有神经元用同一套参数,没有神经调质、电突触、激素和记忆状态。
- 没有实现学习。奖励输入(PAM 多巴胺神经元)可以刺激,但连接强度不会改变,果蝇不会因为玩得久而变强。
- 输入输出映射是人为设计的。画面怎样变成刺激、哪个神经元对应哪个动作,都是开发者规定的,不是从大脑里读出来的。
- 子网络只有全脑的 2%,是按“输入下游且输出上游”筛出来的。完整网络里的其他通路不在其中。
- 追逐与逃生行为是这套模型和这套映射的结果,不等同于真实果蝇的行为。
- 预览图
preview.svg 是矢量示意图,不是实测截图;但网页运行时的栅格全部来自真实仿真。
给 AI 的需求示例
下面是本作品委托 Codex 时需求的精简版。
做一个静态网页游戏,原生 JS ES modules,无构建步骤,无外部资源,中文界面。
大脑必须在 Web Worker 里真跑 LIF 仿真,不能用预设数值。
数据 subnet.json:3330 神经元 / 186406 连接,来自 MaleCNS v1.0。
参数严格对齐 reference_check_subnet.py:dt=0.1ms,阈值 −45mV,
不应期 2.2ms,突触延迟 1.8ms,每脉冲 +权重×0.1mV。
已验证行为(写成测试):
- 左侧逼近 → 逃生≈220Hz,右转≈20Hz
- 左侧目标 → 左转≈50Hz,逃生=0
玩家不能直接操控果蝇,只能布置环境。四关:追逐 / 逃生 / 混战 / 接线实验室。
加真实脉冲栅格、诚实声明面板和 node --test 测试。
小练习
进入接线实验室,把“右侧逼近威胁”这个环境信号改接到 target_R(右侧目标)输入组,再投放一个苍蝇拍阴影。观察果蝇的反应:它现在会朝威胁转过去,而不是逃开。想一想,为什么同一个网络、同一个阴影,只改了输入接线就产生了相反的行为?再打开 js/game.js,找到刺激频率是怎么根据角大小计算的,试着解释为什么阴影越大、转向越强烈。