(@@) Immortal Fruit FlySourceMint a fly

Pebbles: the fly in hardware

Five handheld devices on a table. Each one is a wallet that owns a fly and the body that runs its neurons: the same 155 compass cells that execute on BNB Smart Chain, turned by a gyroscope, cued by magnets, anchored to the chain by transactions the pebble signs itself.

Status. The per-fly core contract, FlyCore, is written and tested (differential against FlyBrain v2, body-versus-poker permissions, seed-once, alive gating in the registry). It is live at 0x77F6066B2ab12072DCEFb7D9DB998944C4ec28C2; every fly page shows its core. The full build document is HARDWARE.md in the repository.

The demo, in ninety seconds

Turn a pebble in your hand.

The compass neurons on its screen, the same EPG, PEN and Δ7 cells that run on BNB Chain, rotate their bump of activity with the gyroscope. The fly knows which way it is facing because real fly neurons say so.

Slide the landmark magnet past its left side.

The hall sensor fires, the EPG neurons of that wedge get a cue, and the bump snaps to the landmark. Slide the spider magnet past instead: a Δ7 shock, the bump collapses, the screen flashes, the fly freezes.

Look at BscScan.

Every 45 seconds the pebble signs FlyCore.stimulate(…) and tick(16) with its own key: the cues it sensed, applied to the same neurons in the EVM. The transaction is from the pebble's address. The fly's page shows the on-chain heading next to the pebble's.

Feed it from your phone.

feed(id, 600) on the fly's page. The pebble chirps “fed 600 s by 0x8a…” and its energy bar refills.

Let one starve.

The screen goes grey: DEAD, brain preserved at block N, a QR code to resurrect it. Resurrect it on the site; the pebble wakes with the identical compass state.

Touch two pebbles together.

BLE finds the neighbour; the current body signs assign(id, neighbour), the neighbour signs accept(id), reads the core state from the chain, and continues it. The fly hopped bodies. On the site: body: Pebble 3, with the whole hand-off in its interaction history.

Mint from the pebble.

A fresh pebble with 1 $FLY and some BNB presses Hatch: it mints its own fly and becomes its body. Hardware that owns an organism.

What makes it more than a gadget: nothing on the pebble is authoritative. The fly's identity, its neurons' state, its energy and its history are on the chain. Unplug the pebble and the fly is still there; hand it to a different pebble, or to the arena, and it is the same fly.

How the pieces fit

Pebble · ESP32-S3gyro → TURN_LEFT / TURN_RIGHThall left / right → CUE wedge 4 / 12magnetometer → weak CUE at northhall “spider” → SHOCKlocal bit-exact replica of the core (preview)screen: ring, bump, fly, energy, chain statusNVS: private key (the pebble's wallet)BLE: find neighbour pebbles → hand-off
BNB Smart ChainFlyRegistry · the organism, ERC-721registerBody / accept / interaction / commit / died / assignFlyCore · per-fly 155 neurons in the EVMstimulate(id, …) / tick(id, n)state: v[155], bias, hist, head, step$FLY · mint and breed burn it; life is BNB

Figure | A pebble is a body: it speaks the registry protocol and runs the fly's on-chain core. The chain is the only authority.

The pebble is a body. It speaks the registry protocol exactly like the arena and DOOM do: registerBody("Pebble 3", uri), waits to be assigned, accept(id), then interactions as things happen, commits on a schedule, died when energy hits zero.

What it runs is the fly's on-chain core. A pebble cannot run the 139,248-neuron whole brain (12 MB of state, sixteen CPU cores at real time). It runs the fly's other brain: the compass circuit that already executes inside the EVM. FlyCore makes that per-fly: every fly in the registry gets its own 155-neuron state in the contract, keyed by token id. The pebble keeps a bit-exact local replica for the display (the same integer model, the same keccak256(step) noise) and anchors it on-chain every ANCHOR_EVERY seconds by sending the cues it sensed and ticking the EVM neurons, then re-reading the state so the replica stays exact. This is the same replica-plus-anchor scheme the website and the DOOM harness already use.

The whole brain does not stop while a fly is in a pebble. It runs on the brain host, described next, which prepares every checkpoint for the pebble to sign; the fly's page then says body: Pebble 3 · whole brain on the brain host and shows its life. Only when the host is unreachable does the pebble fall back to the core alone: it commits with the fly's existing stateRoot and stateURI unchanged (the whole-brain snapshot is preserved exactly), brainStep advanced by the core steps it ticked, and energy decremented by real seconds, and the page says body: Pebble 3 (core only: the whole brain sleeps) until the host, or a whole-brain body such as the arena, takes it up again from that snapshot.

Where the whole brain runs

A pebble is the fly's body on the registry: its address accepts the fly, commits, logs interactions and reports its death, and it signs every one of those transactions with its own key. What it cannot do is run 139,248 neurons. So a brain host on the VPS runs the fly's whole brain and its world on the pebble's behalf, exactly as the arena instantiates a fly: fetch the last committed snapshot from IPFS, check that its sha256 is the stateRoot, load brain and world, and apply feeds and resurrections from the chain. The host is itself a registered body, Brain host, whose uri is its current public origin; a fly id hosted for a pebble lives at <uri>/fly/<id>/…, and a supervisor starts one whole-brain process for every living fly whose body is a pebble. The pebble opens /fly/<id>/ws?lite=1 and receives the fly's life five times a second: where it is, what it smells, what it is doing, when it jumps, eats or is caught. The fly's page on this site opens the same stream. The pebble's senses go the other way: a landmark magnet becomes a puff of odor on that side of the fly, the spider magnet a predator approaching from that side.

The host never holds a key. When it is time to commit, the pebble asks the host for a checkpoint, proving it is the body by signing a timestamped digest with the same key it registered with; the host saves the snapshot, pins it and fresh token metadata to IPFS, and returns the payload (stateRoot, memoryRoot, stateURI, metadataURI, brainStep, energy, historyRoot and the notable events since the last one). The pebble sends commit(…) and the interaction(…)s itself, and died(…) from the host's final payload when the frame says the fly is dead. The chain sees only the pebble; the host is a compute service the pebble reads from and the site watches, and it can be replaced by anyone running the same simulator. The pebble's screen says brain on host · 0.8× real time so nobody mistakes where the neurons are, and its compass view says on-chain. If the host is unreachable the pebble keeps the core running locally and commits the core-only payload as above; if the pebble is unreachable the host keeps the fly living, and the next checkpoint the pebble fetches carries the truth. The protocol, endpoints and payloads are in brain/HOST_PROTOCOL.md.

The per-fly core, in the contract

function tick(uint256 id, uint16 steps) external;                 // anyone, gas only; the fly must be alive in the registry
function stimulate(uint256 id, uint8 ch, uint8 param, uint8 strength, uint16 steps) external;
    // the fly's current body (registry.fly(id).body == msg.sender): gas only, its senses
    // anyone else: burns strength × STIM_PRICE $FLY, a poke
function core(uint256 id) view returns (int16[] v, int8[] bias, uint16[16] headingHist, int32[] pendingInput, uint64 step,
    int32 headX, int32 headY, int64 posX, int64 posY, uint8 stimChannel, uint8 stimParam, uint16 stimStrength, uint64 stimUntilStep, uint64 totalSpikes);
function coreHash(uint256 id) view returns (bytes32);
event Ticked(uint256 indexed id, address indexed by, uint64 fromStep, uint16 steps, uint32 spikes, int32 headX, int32 headY, int64 posX, int64 posY);
event Stimulated(uint256 indexed id, address indexed by, uint8 channel, uint8 param, uint16 strength, uint64 untilStep, uint256 tokensBurned);

No energy, no death, no lineage in the core: those belong to the registry. The parameters are immutable and there is no owner; a curator can only seed a fresh core once, while its step is still zero, which is how fly #1 carries its FlyBrain v2 state over. The kernel is the v2 kernel, unchanged (see how the brain runs on-chain), so a 16-step tick costs about 4.5M gas.

Senses to neurons

Physical inputNeuronsHow
Gyro yaw rate (BMI270)PEN_a / PEN_b, left or right hemisphere (CH_TURN_*)Locally: strength proportional to the rate, applied continuously. On-chain: the net rotation since the last anchor, as one turn stimulus of matching strength.
Magnetometer heading (BMM150)EPG of the wedge facing magnetic north (CH_CUE, weak)The on-chain compass stays anchored to the real world: the pebble's neurons know which way north is.
Hall sensor, left sideEPG wedge 4 (CH_CUE, strong)A landmark on the left.
Hall sensor, right sideEPG wedge 12 (CH_CUE, strong)A landmark on the right.
Hall sensor tagged “spider” (hero pebble, Port C)All Δ7 (CH_SHOCK)The bump collapses, the fly freezes; interaction(id, "shock").
Touch: hold the ringCH_CUE at the touched wedgeA poke, like the website.
Light (camera mean, CoreS3 only)none in v1Later: looming.

Wiring (CoreS3)

PortPinsSensor
Port B (black)G8 = hall left OUT, G9 = hall right OUT, 3V3, GNDTwo hall switches, internal pull-ups, active low.
Port C (blue), hero pebble onlyG17, G18 as GPIOThird and fourth hall sensors (spider, landmark 2).
Port A (red)I2C, freeFuture: more sensors via a Grove hub.

Analog (49E) boards use the same pins, read with analogRead and thresholded at ±15% of the idle value; the firmware auto-detects at boot (a digital switch idles at 3.3 V, a 49E at about 1.65 V). The devices are M5Stack CoreS3 boards (ESP32-S3, 16 MB flash, 8 MB PSRAM, 2.0" 320×240 touch screen, BMI270 gyro and accelerometer, BMM150 magnetometer, Wi-Fi, BLE, 500 mAh battery); a different M5Stack S3 board changes only this pin table and the screen size, because the firmware is written on M5Unified.

Chain cadence and gas

ActionWho signsGasWhen
registerBodypebble~90kfirst boot (name “Pebble N”, uri = the site's fly page)
acceptpebble~60kwhen assigned
FlyCore.stimulate + tick(16)pebble~4.6M ≈ 0.00023 BNBevery ANCHOR_EVERY s (default 45 s → 0.018 BNB/h, about $11/h per pebble; set 120 s for a long day)
interactionpebble~35klandmark seen, shock, hand-off, fed
commitpebble~120kevery 10 min
diedpebble~90kenergy 0
assign (hand-off)the current pebble~50ktouch
mint (hatch)pebble~250k + 1 $FLYbutton

Load each pebble with 0.05 BNB (about two hours of anchoring at 45 s plus everything else) and, for hatching, 2 $FLY. The pebble shows its address and a QR code at boot. Gas is the cost of the story: anchoring the EVM neurons is what makes the demo real, and the cadence knob is there so a day of demos does not cost a day of BNB. Between anchors the screen is a preview, exactly as on the site; the two agree at every anchor or the pebble resyncs and says so.

Bring-up

  1. pip install platformio (or use the repository's venv), cd firmware, copy include/secrets.example.h to include/secrets.h and fill in the Wi-Fi.
  2. pio run -e cores3 -t upload with a pebble on USB-C. It boots, shows Pebble 1 · 0xABCD… and a QR code.
  3. Send it 0.05 BNB. It registers itself; watch the transaction on the screen and on BscScan.
  4. On the site, open a fly you own, Hand it to a body, choose “Another body address…” and paste the pebble's address. The pebble accepts within a poll and the ring lights up.
  5. Rotate it. Bring the magnets. Feed it from the site. Repeat for the other four.
  6. Two pebbles: hold Hand off on the one hosting a fly, tap the neighbour when it appears, and watch the fly move.

The private key is generated on first boot from the ESP32 hardware RNG and never leaves the device; holding both touch buttons under the screen for three seconds shows it once, for backup. The rest of the design, the risks, and the honest caveats are in HARDWARE.md.