Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

Explore the **NETSPHERE** in a local browser, inspect one anatomical flybody animal,
and stimulate a **127,400-neuron FlyWire reference model** while watching measured
neural activity. The browser currently keeps neural signals and body actuators
disconnected. [Open the development environment](#interactive-environment-in-the-browser)
or read the [neural model and its limits](docs/neural-reference.md).
neural activity. An experimental **MN9-to-proboscis link** now drives one body
actuator from simulated motor-neuron spikes, with explicit baseline and blocked-link
controls. [Open the development environment](#interactive-environment-in-the-browser)
or read the [motor link and its limits](docs/motor-link.md).

The animal is the anatomically detailed [flybody](https://github.com/TuragaLab/flybody) model of *Drosophila melanogaster* (Google DeepMind and HHMI Janelia, *Nature* 2025). In the separate recorded-flight pipeline, its pretrained controller runs on CUDA, MuJoCo Warp integrates the body and wing aerodynamics at 20 kHz, and a geometric navigator steers the fly through the collidable interior. Every frame of those recordings comes from the integrated physical state.

Expand All @@ -24,8 +25,9 @@ The full 60-second take, its metrics and the validation files are attached to th
- It **is** whole-body physics: joints, wings with ellipsoid fluid forces, and the official DMPO flight policy (wingbeat pattern generator plus a residual MLP) driving the actuators.
- It **is** a real 3D world: walls, pillars, ducts, cables and walkways with collision in the same MuJoCo model that integrates the fly. The camera moves through that space.
- The **recorded flight** uses the official MLP policy. The browser development lab
also runs stimulus-response trials on the published FlyWire 630 connectome;
those neural signals are **not yet connected to the body's muscles**.
runs the published FlyWire 630 connectome and an experimental sugar-response
motor link. Its firing-rate-to-servo adapter is engineered; world sensing,
neural walking and neural flight remain unimplemented.
- The navigator is **not** learned vision. It reads the known world geometry and the measured position at 100 Hz and picks turns and climbs with clearance for wings and body. It only changes the reference command; it never writes the animal's pose or velocity.

## Results
Expand Down Expand Up @@ -78,20 +80,23 @@ verify_take.py independent checks: duration, decoded frames, clearance, ha

### Interactive environment in the browser

The local browser observatory contains **one passive flybody animal**, selectable
The local browser observatory contains **one physical flybody animal**, selectable
with a close-up orbit camera, plus three city viewpoints and a gravity/contact
experiment. Its **neural controller is disconnected and actuator drive disabled**:
it settles physically, with no autonomous walking or flight policy.
The **Neural activity** panel runs a separate FlyWire 630 reference assay with
127,400 neurons and all 14,687,178 stored directed connections. It shows calculated
spikes, selected real connections and the workflow up to the disconnected muscles.
Stimulate antennal neurons, compare a baseline or block sensory output.
experiment. It settles passively between on-demand trials, with no autonomous
walking or flight policy. Open **Neural activity → Stimulate sugar neurons** to
run the entire FlyWire 630 graph (127,400 neurons, 14,687,178 stored connections)
and drive the rostrum from its two MN9 motor neurons. **View proboscis** focuses
the observer on the head. Neural and physical time advance together in this
500 ms experiment. Compare no input or a blocked motor link; the UI retains
measured spikes, drive and joint motion after completion.
The expandable antennal reference assay retains its separate clock and readouts.
Three.js renders measured body poses on demand; native MuJoCo sleep reduces idle
work. The backend is capped at 2 CPUs and 1 GiB. The browser requests
high-performance GPU graphics and targets 30 FPS while moving, with bounded
resolution; backend physics and neural computation remain on the CPU.
[Anatomy preparation, controls and limitations](docs/browser-environment.md) ·
[Neural preparation, numerical validation and limitations](docs/neural-reference.md).
[Neural preparation and numerical validation](docs/neural-reference.md) ·
[Motor protocol, engineered adapter and causal checks](docs/motor-link.md).

```bash
pnpm-docker install --frozen-lockfile # provisioned Socket-protected Docker launcher
Expand Down Expand Up @@ -173,12 +178,14 @@ scripts/
verify_take.py independent acceptance checks on states, geometry and video
verify_avoidance.py compare baseline, shifted-obstacle and avoidance-off runs
city_world.py procedural collidable megastructure
serve_environment.py local browser service, one passive fly and live gravity test
serve_environment.py local browser service, one physical fly and live gravity test
dev_environment.py container-owned backend watcher; one dev stack on port 8089
neural_reference.py incremental LIF dynamics over the full FlyWire 630 graph
neural_lab.py bounded on-demand trials and measured activity telemetry
motor_bridge.py shared-clock sugar → FlyWire → MN9 → native rostrum servo
validate_motor_bridge.py causal motor controls, actuator isolation and clock checks
fetch_neural_reference.py, prepare_neural_reference.py, validate_neural_reference.py
passive_fly.py cached anatomy attachment, disabled actuator drive
passive_fly.py cached anatomy attachment and passive initialization
prepare_browser_fly.py bounded anatomy preparation, full-resolution mass properties
test_environment.py physical contact, sleep/wake, state isolation and telemetry checks
city_navigation.py receding-horizon geometric navigator
Expand Down
35 changes: 25 additions & 10 deletions docs/browser-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ vertically, **Shift** to move faster and the mouse wheel to move forward/backwar
The three city view buttons return to known observation points. Touch screens have
movement buttons and support dragging to look.

The animal is a **passive physical body with its neural controller disconnected**.
All actuator forces are disabled; anatomical springs, joints, gravity and contact
remain active. It settles under physics; it does not walk or fly autonomously.
The **Neural activity** panel opens a separate connectome reference assay:
The animal settles passively and has **one experimental neural motor link**.
Open **Neural activity → Stimulate sugar neurons** to route calculated MN9 spikes
through an engineered adapter to the native rostrum servo. **View proboscis**
focuses the observer on the head. Compare **Baseline · no input** and **Block motor
link**; **Stop** removes actuator authority. New trials wait for physical rest,
and pause/resume affects both clocks. All other actuators remain disabled.
Anatomical springs, joints, gravity and contact stay active. It does not walk or
fly autonomously. [Motor protocol and causal validation](motor-link.md).

The expandable **Antennal reference assay** retains a separate connectome assay:
antennal stimulus → FlyWire LIF dynamics → neural readouts → disconnected muscles.
Its structure view shows a selection of actual connections with schematic positions;
the entire published graph is retained in the simulation. Counts and voltages come
Expand Down Expand Up @@ -94,7 +100,7 @@ activating the `dev` profile cannot start a flight recording in the background.
103,807 triangles (source: 272,550), with maximum original-vertex displacement
15.2 µm, and a 2.56 MiB binary packet before HTTP compression.
The 67 physical segments have a total mass of approximately 0.985 mg.
- Native MuJoCo sleep is allowed for this disconnected stage, at the default
- Native MuJoCo sleep is allowed between motor trials, at the default
0.001 cm/s tolerance. The attachment frame's rotational sleep length is corrected
from its distance to the world origin to a conservative anatomical collision
radius (0.286 cm). This changes sleep eligibility, not forces or integration.
Expand All @@ -105,7 +111,9 @@ activating the `dev` profile cannot start a flight recording in the background.
This accuracy/idle-cost tradeoff is specific to the passive viewer, not a
connectome or controlled-locomotion validation.
See [MuJoCo sleeping semantics](https://mujoco.readthedocs.io/en/stable/programming/simulation.html#sleeping-islands).
Connecting actuators later requires explicit wake handling.
The motor bridge uses MuJoCo's documented negative-zero applied-force wake
signal, adding no nonzero force. The fly tree cannot sleep while driven;
completing or stopping the trial restores passive sleep eligibility.
- One backend simulation thread advances independently of browser tabs. Server
events target changed body poses at up to 30 Hz and idle status at 1 Hz.
Unchanged poses and trails are omitted per viewer; reconnect starts with a full
Expand All @@ -115,6 +123,8 @@ activating the `dev` profile cannot start a flight recording in the background.
6 ms of thread CPU, checked between native stepping blocks, so costly body
settling cannot hold up every state read for a full 250-step batch.
Only executed steps are subtracted from the bounded accumulator.
A coupled motor trial advances one neural and one physical step together,
inside that same physics-thread budget. It adds no second simulation worker.
While settling, physical time may advance slower than wall
time; the footer reports the measured ratio. Resting bodies resume near 1×.
Pause and 0.25× speed affect the
Expand Down Expand Up @@ -152,13 +162,15 @@ activating the `dev` profile cannot start a flight recording in the background.

The server only serves an allowlist of frontend assets and read endpoints:
`/api/world`, `/api/state`, `/api/events`, `/health`, plus `/api/dev-version` in
development mode. `POST /api/command` accepts bounded drop, pause, speed and
`neural_trial` commands with same-origin JSON. Neural trial mode must be
`stimulus`, `baseline` or `blocked`; one trial runs at a time. It cannot read
development mode. `POST /api/command` accepts bounded drop, pause, speed,
`neural_trial`, `motor_trial` and `motor_stop` commands with same-origin JSON.
Trial mode must be `stimulus`, `baseline` or `blocked`; only one neural or motor
trial runs at a time. Motor commands cannot select arbitrary neurons, actuator
names, gains, torques or durations. It cannot read
arbitrary project files or edit the scene. At most eight event streams are open
at once. The container has a read-only filesystem and limits of 2 CPUs, 1 GiB
RAM and 64 PIDs. These limits cover the backend; the browser is a separate process.
The neural worker has an additional duty budget of 0.15 core, runs only on request
The separate antennal worker has a duty budget of 0.15 core, runs only on request
and publishes changes at up to 10 Hz. It adds no continuous scene render loop.
This local server is not intended to be exposed directly to the internet.

Expand All @@ -178,6 +190,9 @@ They check the unchanged anatomical contracts, disabled actuation, fly contact,
native sleep and force-triggered wake, and compare sleep with 200 ms of awake
dynamics (segment drift must remain below the measured visual reduction
displacement, currently 15.2 µm). Delta telemetry is also checked.
The [motor validation](motor-link.md#validation) separately tests the full graph
against no-input and blocked-link controls, exact clock alignment, sleeping-body
wake, the actuator allowlist, cancellation and failure handling.
Browser checks cover visible rendering, view changes, mouse/keyboard motion,
pause/resume, slow motion, the gravity experiment, reconnect and mobile layout.
The measured passive-viewer sample below predates the neural panel. The later
Expand Down
138 changes: 138 additions & 0 deletions docs/motor-link.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Experimental MN9-to-proboscis link

The browser can now turn **simulated FlyWire motor-neuron spikes into a physical
flybody joint response**. This first link uses sugar-responsive sensory neurons
and the MN9 motor pair. It drives only the rostrum, part of the proboscis.

Open **http://localhost:8089 → Neural activity → Stimulate sugar neurons**.
**View proboscis** focuses the observer on the head; drag to choose an angle.
The panel retains measured MN9 spikes, adapter drive and joint angle after the
movement. Compare **Baseline · no input** and **Block motor link**.
New trials wait for the body to settle naturally. **Stop** cancels the trial;
the footer pauses or slows both physical and neural time together.

```mermaid
flowchart LR
S["21 sugar GRNs<br/>Direct input"] --> C["Full FlyWire 630 graph<br/>127,400 LIF neurons"]
C --> M["MN9 left and right<br/>Calculated spikes"]
M --> A["Engineered adapter<br/>Filtered rate → servo target"]
A --> B["Native flybody rostrum<br/>MuJoCo joint dynamics"]
```

This is an on-demand motor experiment. Input is delivered directly to identified
neurons; no sugar object, taste receptor mechanics, world sensing or physical
feedback into the brain is implemented. There is no autonomous walking/flight
controller or complete ventral nerve cord. The antennal assay remains separately
available and has no motor authority.

## Biological source and engineering boundary

The pinned [Shiu/Spiller notebook](https://github.com/philshiu/Drosophila_brain_model/blob/91bdd1e7dcf193f3e7ca5a8933497fcef63b7960/figures.ipynb)
identifies 21 right labellar sugar GRNs (`neu_sugar`) and two MN9 neurons
(`ids_mn9`). The runtime verifies that notebook's SHA-256 against the graph
manifest and extracts literal IDs without executing cells. The motor IDs are
`720575940660219265` (left) and `720575940645521262` (right).
The [neural reference guide](neural-reference.md) describes the entire retained
graph, LIF equations, numerical agreement and source licenses.

MN9 innervates the rostrum protractor muscle, providing a documented motor
association for this first link. Proboscis movement involves additional muscles
and joints; connecting one servo does not reconstruct that complete system.
See [McKellar et al., *Controlling motor neurons of every muscle for fly proboscis reaching*, eLife 2020](https://elifesciences.org/articles/54978).

The **adapter is our engineering approximation**, not a measured neuromuscular
transfer function or a learned policy. The bilateral MN9 pair is averaged into
one existing flybody rostrum hinge. Each spike contributes to a 30 ms exponential
rate estimate; a mean 100 Hz maps to full drive:

```text
rate[t] = rate[t-1] × exp(-0.1 / 30) + MN9_spikes[t] × 1000 / (2 × 30)
drive[t] = clamp(rate[t] / 100, 0, 1)
rostrum_target[t] = 0.183 + drive[t] × (-1.24 - 0.183) radians
```

The existing position servo, gain, force limit, joint limits, springs, inertia,
contacts and friction calculate the response. No runtime pose or velocity writes
imitate movement. Actuation stays entirely off before the first MN9 spike.
The waveform and gain are not calibrated against an animal's measured kinematics.

## Protocol and isolation

Each trial resets neural state and uses seed `20260912`, while preserving the
body's integrated state and global physical clock. It lasts **500 ms**: 300 ms
of 200 Hz input per sugar neuron, then 200 ms without input. The published IDs
are reused; this specific finite-duration protocol is a local assay, not a claim
to reproduce every published feeding experiment.

| Trial | Neural input | Motor link |
|---|---|---|
| Sugar response | Fixed sugar events | MN9 drives the rostrum adapter |
| Baseline | None | No spikes and no actuator drive |
| Block motor link | Identical sugar events | Normal brain activity; actuation disabled |

One neural step precedes one native physical step, both **0.1 ms**, on the same
worker. A mismatch stops the trial. Pause freezes both clocks; paused time does
not consume the 90-second active wall-time allowance. Other guards are 10 CPU
seconds and 100,000 spikes, checked every 25 steps. Stop, completion and exceptions
disable actuation and release neural dynamic state.

Actuator groups allow only `fly_001/rostrum`; every other actuator is disabled.
A sleeping body is woken using MuJoCo's documented **negative-zero applied-force
signal**, which adds no nonzero external force. Sleep is disallowed while the
servo is driven and restored afterwards. See the [MuJoCo sleeping documentation](https://mujoco.readthedocs.io/en/stable/programming/simulation.html#sleeping-islands).
The body then settles under its passive dynamics.

The graph is shared read-only with the antennal lab. Trials are mutually exclusive,
use the existing 0.9-core physics duty budget and keep the dev ceiling of
2 CPUs / 1 GiB. Telemetry is bounded to 100 measured samples, emitted only when
changed; the browser adds no continuous idle animation. No new dependencies,
GPU compute allocation or persistent container is required.

## Validation

Run the causal checks in the existing disposable tooling image:

```bash
LOCAL_UID="$(id -u)" LOCAL_GID="$(id -g)" \
docker compose --profile neural-tools run --rm --no-deps \
-e FLY_NEURAL=1 neural-tools \
python scripts/validate_motor_bridge.py --output /out/motor-validation.json
```

The comparison clones the same settled **test** state for each intervention using
MuJoCo's complete data copy. The live service never resets the fly for a trial.
Checks cover unchanged state at start and before the first motor spike, shared
clocks, native wake, excluded actuators, concurrent trial rejection, cancellation
while paused, delta telemetry and failure on clock mismatch.

Measured on 2026-09-12:

| Trial | MN9 left / right spikes | Downstream spikes | Peak rostrum movement | Other actuator force |
|---|---:|---:|---:|---:|
| No input | 0 / 0 | 0 | 0° | 0 |
| Motor link blocked | 30 / 17 | 3,839 | 0° | 0 |
| Sugar response | 30 / 17 | 3,839 | 40.03° | 0 |

The first motor force occurs at the first MN9 spike, **25.5 ms of simulated
time**, not wall time. Both stimulated trials have 5,076 total spikes, including
1,237 input spikes. Neural and physical clocks each advance 500 ms; all checks
complete without physics warnings. These establish a causal software-to-actuator
path, not biological equivalence, real-time performance or autonomous behavior.

The ignored `out/neural-reference/motor-validation.json` records code/cache
hashes, protocol, counts, force, clock alignment, CPU/wall time and peak RSS.
The isolated validation process peaked near 215 MiB RSS; the stimulated 500 ms
run used about 2.3 CPU seconds. After the browser trials, a short resting-service
sample showed 149.8 MiB and 8.43% of one CPU core. These are observations of
specific workloads, not performance guarantees.

Browser checks exercised all three modes, head focus, pause/resume with both
clocks frozen, cancellation with drive off, and the retained antennal assay
(2,906 total / 706 downstream spikes). The baseline and motor-blocked trials
added no new 3D frames to the settled scene. Desktop and 390×844 mobile panels
were inspected with no horizontal overflow. All 295 captured requests returned
HTTP 200, with no JavaScript exceptions. Resizing the software-rendered viewport
caused a graphics-context reset that recovered. This does not establish hardware
FPS. The isolated browser was closed after validation.

The [browser guide](browser-environment.md) covers rendering limits and operation.
Loading
Loading