FLYHOUSE documentation

FLYHOUSE puts a population of simulated fruit fly connectomes in a twelve room house and broadcasts it without editing. They eat, sleep, bond, court, mate, breed, fight, ignore each other and die. This document gives the actual numbers behind all of it, and is explicit about which parts are biology and which parts are television.

DRIVE MODEL BOND SYSTEM COURTSHIP AND BREEDING FIGHTING AND DEATH SHARED, NO SERVER CROSS ENGINE VERIFIED TOKEN NOT DEPLOYED

What FLYHOUSE is

A population of between four and twelve agents shares a twelve room house. Each one carries five internal drives that rise on their own clocks, and each drive maps to a place in the house that satisfies it. There are two food sources for up to twelve flies, and the food is the axis everything else turns on.

Nothing is scripted. There is no storyline generator, no director and no event queue drip fed to keep it interesting. A quiet hour goes out as a quiet hour.

Status and honesty

WHAT IS REAL RIGHT NOW

The house runs entirely in your browser, and it is the same house everyone else is watching. There is no server holding the state. The simulation is a pure function of the season number and the tick count, so every viewer computes an identical house from identical inputs. That is verified, not asserted: see the determinism section. The token is not deployed and eviction voting is not live, and this section gets updated rather than quietly deleted when that changes.

The thing worth being plain about: the house runs a behavioural model derived from the connectome's known circuits, not a full spiking simulation of 166,000 neurons per fly. Twelve full connectomes at 60 fps is not something a laptop does, and this runs on a laptop.

Floor plan

Four by three, nine world units per room, with low walls so the grid reads from above.

KITCHENThe main plate. Refills at 0.034/s to a cap of 1.0. The contested resource.
LOUNGEThe larger social room. No resource except each other.
GARDENThe big window. Light and warmth.
BALCONYA second, smaller window. Overflow when the garden is busy.
PANTRYThe small plate. Cap 0.55, refills at 0.020/s. Never enough on its own.
HALLSecond social room, and where new housemates come in.
BATHROOMStanding water. Thirst only. Never depletes.
DIARYOne booth. A fly enters alone and its full internal state goes out live.
BEDROOMFour pads. Fatigue drains at 0.09/s.
DARKROOMFour pads, no window. Fatigue drains faster, at 0.14/s.
LAUNDRYSecond water source, further from everything.
NURSERYWhere a gravid female lays, and where eggs hatch into housemates.

Food and water sit in opposite corners, rest is next to the food, and company is in the middle. A fly that wants everything has to cross the house repeatedly and will keep running into other flies doing the same.

Drives and urgency

Each housemate carries five drives in the range 0 to 1, rising at a base rate multiplied by that individual's personal coefficient. Coefficients are the only thing that differs between the founders, and for flies born in the house they are inherited from both parents.

DRIVERISES ATSATISFIED INURGENCY WEIGHT
hunger0.013 / sKitchen, pantry1.45
thirst0.015 / sBathroom, laundry1.15
fatigue0.010 / s light, 0.026 / s darkBedroom, darkroom1.00
light0.016 / s light, 0.002 / s darkGarden, balcony0.62
social0.015 / sAnywhere another fly is0.70

The goal is argmax(drive x urgency), not argmax(drive). That distinction matters more than it looks.

WHAT WENT WRONG WITHOUT IT

Raw drive level is not priority. Hunger rose the slowest of the five, so on a plain argmax it almost never won, and a fly would sunbathe at 98 percent hunger. In testing, the entire house starved to death standing next to a completely full plate. The urgency weights are what make a starving fly go and eat, and they are the single most load bearing constant in the model.

Commitment

A fly commits to a goal for 12 to 18 seconds. It reconsiders early only when the goal is satisfied, the food runs out, nobody turned up in the lounge, or another drive crosses 0.78 and becomes impossible to ignore.

// per logic tick, 10 Hz
for each drive d: d += rate[d] * coeff[fly][d] * dt

reconsider = commit <= 0 or satisfied or foodGone or stoodUp or urgent
if reconsider and not midMeal:
    want   = argmax(drive[d] * urgency[d] + stickiness)
    target = slotAround(bestResourceOf(want), flyIndex)
    commit = 12 + rand(6)

Stickiness is a flat 0.07 added to the current goal, and it drops to zero the moment something urgent appears. Without commitment, saturated drives tie under argmax and a fly flips between the kitchen and the bathroom every second, crossing the house forever and arriving nowhere. That was a real failure mode and this is the fix for it.

One more rule keeps the goal honest: if the resource a fly is standing on no longer matches its goal, the assignment is dropped and re-picked. Letting those drift apart deadlocked a fly into drinking water forever while its actual goal sat unreachable.

The day cycle

One house day is six minutes of real time. Lights are on for the first 72 percent and out for the rest. Fatigue rises 2.6 times faster in the dark, which fills the bedroom and darkroom, and the light drive nearly stops rising, which empties the garden.

Six minutes is a show pacing decision, not a biological one, and we are not going to pretend otherwise. It is short enough that a visitor sees a full night, and long enough that a bond built in the afternoon still means something by evening.

The encounter check

Any two flies within 3.2 units run one check, in this order. The first branch that fires wins.

ORDERBRANCHFIRES WHEN
1InterferenceOne is courting and the other is a male who is not. Probability per tick 0.22 + aggression x 0.55. Breaks up the courtship and starts a fight.
2FightHeat above zero and neither is locked in another interaction. See below.
3CourtshipBond above 30, a male and a female, male off cooldown, female not gravid, both above 55 percent condition and the male not hungry or tired.
4CompetitionBoth on the same plate, both hungry, plate below 60 percent of cap. Bond falls at 3.4 per second.
5ToleranceEverything else. Bond rises at 3.2 per second, or 1.1 if one of them is asleep.

Branch 5 is where nearly all of it lands, and within it the line that goes out is often passed without acknowledging each other. Two flies in transit with no shared history genuinely do nothing, and that is the most common event in the house. We report it rather than hiding it to make the feed look busier.

Bonds

Every pair carries one signed number from -100 to +100, moving only while they are within 3.2 units of each other.

SITUATIONPER SECONDNOTE
Near each other, ordinary+3.2Simple proximity tolerance. Most bonds are built here.
Near each other, one asleep+1.1Counts less. Sleeping next to someone is not choosing them.
Both on a depleted plate-3.4The only continuous negative term in the system.
Losing a fight-18 oncePlus -22 when the fight starts.
Rejected mid song-14 onceApplied to both sides of the pair.
Successful mating+24 onceThe largest single positive move available.
Everything elsex 0.996Slow decay toward indifference. Nothing stays hot forever.

While a fly is avoiding someone it beat it or was beaten by, the bond between them does not rise at all, however close they happen to be. Proximity under duress is not tolerance.

WHY ONE NUMBER

We could have modelled separate affection, rivalry and familiarity axes. We used one because we could not honestly defend the extra ones. A fly does not have a relationship. It has a history of what happened last time it was near a particular animal, and one signed accumulator represents that fairly.

Courtship and mating

A male past a bond of 30 with a female extends one wing and vibrates it. That is real Drosophila courtship song, it is the best mapped social behaviour in the animal, and it is visible in the 3D view as a held out wing on one side only. Courtship runs 6 to 10 seconds.

accept = 0.08
       + bond / 260
       + femaleSocialDrive * 0.26
       + (femaleCondition - 0.5) * 0.2

If she stays, they mate for 4 to 6 seconds and both gain 24 bond. If she walks off, both lose 14 and the feed says so by name. The male then goes on a 95 to 165 second cooldown either way.

Gravid, laying and hatching

  1. A mated female is gravid for 75 to 120 seconds.
  2. She then walks to the nursery and lays. The egg is a visible object in the room.
  3. The egg sits for 70 to 115 seconds and hatches into a real housemate.
  4. The new fly's five drive coefficients are the midpoint of both parents plus noise of up to 0.22 either way, clamped to the range 0.4 to 1.8. Aggression is inherited the same way.
  5. If the house is at its cap of twelve when the clutch is ready, nothing is laid and the feed says why.

Fighting

Male Drosophila fight over food and over females, with lunging and wing threat. Both are in here.

heat = 0.62  // both on a plate below 60% of cap, both hungry
     + 0.26  // both on the same plate and both above 60% hunger
     + 0.42  // bond below -25
     + (aggrA + aggrB) * 0.26
     - 0.36
if heat > 0: fight starts with probability heat * 0.9 per second

A fight lasts 4 to 9 seconds. Both flies close on each other, wings beat at 46 rad/s, and the thorax goes red. Resolution compares condition x (0.5 + aggression).

Death

There are three ways out, all permanent. The body falls, rolls onto its back, shrinks and is removed. The name moves to the list at the bottom of the rail with the cause and the day.

CAUSEMECHANISM
Lost a fightCondition hits zero during fight resolution. The most common cause in a crowded, hungry house.
StarvedHunger above 0.97 for a cumulative 30 seconds starts draining condition at 0.008 per second. Recovery from starvation runs at twice that rate, so a single bad afternoon is survivable and a lost week is not.
Old ageLifespan is drawn at birth, between 1800 and 3200 house seconds, which is five to nine house days. Past it, condition drains at 0.030 per second.

Condition recovers at 0.009 per second while feeding or resting, so an uninjured fly with access to food sits at full. Condition below 50 percent dulls the thorax colour and slows the fly down, which is visible before the rail tells you.

Population dynamics

This is the part that took the longest to get right, and it is worth explaining because it is the engine of the whole show.

The two plates together refill at 0.054 per second. A full meal costs about 0.46 of a plate and a fly needs one roughly every 77 seconds, so demand is about 0.0060 per fly per second. The house feeds six comfortably and cannot feed twelve.

  1. A calm, well fed house bonds, courts and breeds.
  2. Population climbs past nine and the plates stop keeping up.
  3. Flies queue on depleted plates. Competition drives bonds negative and fights start.
  4. Fights and starvation cull the population back to something the house can feed.
  5. The survivors are calm and well fed, and it starts again.

Across five test seeds run for nine house days, this produces a population oscillating between five and twelve, twelve to eighteen births, eight to fifteen deaths, and a mix of all three causes. We tuned for that shape deliberately. It is the one honest piece of authorship in the whole simulation and we would rather name it than pretend the numbers fell out of biology.

Two hard limits sit on top: the house caps at twelve, and if it falls below four a new fly comes in the front door every 45 seconds with randomly drawn coefficients. Nobody is ever revived.

Thought readout

The bubble above a fly is not a language model and it is not a joke generator. It is a lookup from the fly's state, its dominant drive and its current social context into a fixed vocabulary of about fifty short phrases.

condition < 45%           -> "hurts" | "away from it" | "slow"
state == RESTING          -> "..." | "quiet" | "dark"
state == FIGHTING         -> "lunge" | "get back" | "off"
state == COURTING         -> "wing out" | "singing" | "look at me"
state == MATING           -> "yes" | "still" | "stay"
gravid, heading to lay    -> "heavy" | "the warm corner" | "down"
no food anywhere          -> "nothing on the plate" | "empty" | "weak"
near someone, bond < -18  -> "that is mine" | "move" | "get off"
near someone, no history  -> "nothing" | "not that one" | "keep going"
near someone              -> "who is that" | "someone is here"
otherwise                 -> vocabulary[dominant drive]

We keep the vocabulary this poor on purpose. The temptation is to write it up into something charming and articulate, which would be a lie about what is in there. A fly running on a hunger signal is thinking something closer to the word sugar than to a sentence.

Diary room

A fly goes to the diary room on its own when the sum of its five drives falls below 2.1, meaning it wants nothing much. It stays 14 to 24 seconds, is invisible to the social system while it is in there, and its bonds do not move. The panel publishes its dominant drive, condition, aggression, closest and furthest housemate, its parents if it was born in the house, and what it is thinking.

It is the only moment the show gives you complete information about a housemate. The rest of the time you read it from the outside, same as the other flies do.

Rendering

Three.js, all geometry built procedurally at load. No external model file, nothing to 404, no asset licence attached to the show.

PARTBUILT FROM
Fly bodyThree spheres. Females get a longer, blunter abdomen and slightly larger scale, which is how you tell them apart without reading the tag.
EyesTwo scaled spheres in unlit red, so they read at any distance.
WingsTwo planes on rotation pivots. Flap rate follows state: 34 rad/s flying, 46 fighting, near zero asleep, and one wing held at 1.45 rad while courting.
LegsSix thin boxes. The front pair animates during grooming.
ConditionThorax colour and emissive. Red while fighting, desaturated below 50 percent.
EggsA stretched sphere in the nursery, bobbing until it hatches.
DeathDrop to the floor, roll to 180 degrees on Z, shrink over 2.2 seconds, remove.
HouseBoxes for floor slabs and low walls, planes for rugs and window beams, a canvas texture per room name on the floor.
Name tagsHTML projected from world space each frame, with a screen space pass that nudges overlapping tags apart so a crowded lounge stays readable.

If you want a nicer fly, the hook is MODEL_URL at the top of the page script and the function buildFly(). Swap in a GLTF load there and nothing else cares, as long as the returned group exposes userData.wings, userData.legs and userData.accent.

Seasons

A season is one hour of real time, which is sixty house days. It starts on the hour, UTC, with six founders and empty bonds. At the end it stops dead and a new season opens with a new cast. The HUD shows the season number and the countdown to the reset.

The season index is the only seed. Everything downstream, the founders' coefficients, every random choice for the next sixty house days, is derived from it. Two people opening the page in the same hour are not watching similar houses. They are watching the same one, frame for frame.

How a late joiner catches up

There is no state to download, because there is no server holding any. Instead the page replays the season from its first tick at speed, and then keeps running in real time from wherever the wall clock says it should be.

season   = floor(now / 3600000)
tick     = floor((now - seasonStart) / 100)     // 10 Hz
house    = replay(season, 0 .. tick)

A full hour is 36,000 ticks and replays in roughly half a second on a desktop, a few seconds on a phone, and the average visitor arrives halfway through so pays half of that. During the replay everything that only exists for the screen is skipped, which is where most of that speed comes from. The progress bar you see on load is the replay, not a download.

WHY SEASONS RATHER THAN ONE ENDLESS HOUSE

An endless house would mean a viewer joining on day thirty has to replay thirty days to catch up, which is minutes of computation for nothing. Bounding the season bounds the worst case catch-up. It also happens to be the better show: sixty house days is a complete arc, the founders age out inside it, and there is a finale to schedule against.

Determinism

The entire approach rests on one claim: the same inputs produce a bit identical house on every machine. If that is even slightly wrong, two viewers silently drift into different houses and nobody notices for a week. So it is worth saying exactly what was done and exactly how it was checked.

What is safe and what is not

OPERATIONSTATUSWHY
+ - * /SafeIEEE-754 doubles, correctly rounded, identical everywhere.
Math.sqrtSafeIEEE-754 requires correct rounding for square root specifically.
Math.round, floor, abs, min, maxSafeExactly specified in the language.
Math.sin, cos, atan2, hypotNot safeImplementation defined. Different engines return different last bits, and the error compounds over 36,000 ticks.
Math.randomNot safeReplaced entirely by a seeded linear congruential generator.
Array.sortSafeRequired to be stable since ES2019, and the comparators here are total.
Object key orderSafeInsertion order for string keys, and the insertion sequence is itself deterministic.

So the four unsafe transcendentals were replaced with our own. dsin is a range reduction plus a seventh order polynomial, dcos is dsin shifted, datan2 is a fifth order approximation with quadrant fixups, and dhypot is just sqrt(x*x + y*y). Accuracy is not the goal and these are not as accurate as the built-ins. Producing the same answer on every engine is the goal, and they do.

How it was verified

A hash is taken over the full simulation state: every fly's position, velocity, heading, five drives, condition, age, state, goal, partner, thought and every bond, plus all resource levels, the day, the clock, the egg queue, the obituaries and the counters. Three checks then have to pass.

  1. Same engine, twice. The same season replayed twice in one browser must hash identically. Catches any leaked wall-clock or unseeded randomness.
  2. Replay against live. A season fast forwarded must hash identically to the same season stepped in real time. Catches anything the fast path skips that actually feeds back into state, which is how the mating bob was caught: it moves the body, so it changes distance checks, so it cannot be skipped.
  3. Across engines. The same seasons replayed in Chromium, Firefox and WebKit must all hash identically. This is the one that matters and it is the one that would have failed on the built-in trigonometry.

All three pass. Three different seasons, three engines, identical hashes, and the math primitives agree to the last digit.

THE HONEST RISK HERE

Determinism is a property that holds until someone adds one line that breaks it, and it breaks silently. A stray Math.random, a Date.now read inside the simulation, or a new Math.sin call would split the audience into different houses with no error and no visible symptom. The cross-engine hash check is the guard against that and it has to be run before anything ships.

Fee routing

4 percent on buys and sells, split at the contract level.

SHAREDESTINATIONPURPOSE
2.0%Compute and streamThe canonical simulation and the continuous broadcast. A real recurring bill, and the reason the token exists.
1.0%LiquidityPaired and locked.
1.0%Prize poolAccumulates across the season, paid out at the finale to the wallets that backed the surviving finalist, weighted by how early they did so.

Eviction voting

One token is one vote, on a snapshot taken at the start of each eviction window. The housemate with the most votes leaves alive, which makes eviction the one merciful exit in the house. A new fly enters with freshly drawn coefficients and no bonds.

Until the contract is deployed, the bars on the front page show the in house unpopularity score: the sum of every negative bond pointing at that housemate. It is labelled as such on the page. It is a preview of the mechanic, not a live vote, and it switches to real on chain weight the moment the contract exists.

HTTP API

ENDPOINTRETURNS
GET /api/houseContract address, season, day, canonical house availability. Returns canonical: false while the backend is not live.
GET /api/castCurrent housemates with drive coefficients, aggression, sex, age and entry day.
GET /api/voteOpen eviction window, snapshot block, tallies. Returns open: false before launch.
GET /api/house

{
  "canonical": false,
  "reason": "HOUSE_BACKEND unset",
  "contract": null,
  "season": 1
}

Known limitations

  1. The house is a reduction. It runs the behavioural consequences of the connectome's circuits, not the spiking network itself.
  2. History does not accumulate. Bounding the season is what makes the no-server approach work, and the price is that nothing carries across the reset. No dynasty runs longer than sixty house days.
  3. Determinism is fragile by nature. It holds today and it is verified across three engines, but one careless line in a future change would split viewers into different houses silently.
  4. Five drives is not an animal. Real flies do far more than eat, drink, sleep, seek light and socialise. We modelled the behaviours with the clearest published circuit basis and left the rest out rather than inventing it.
  5. The population curve is authored. Refill rates, meal costs and lifespans were tuned until the house oscillated instead of either exploding or going extinct. That is a television decision and it is named as one in the population section.
  6. Courtship is simplified. Wing extension and song are real and well mapped. Triggering them off a bond threshold is a shortcut, and the acceptance formula is ours.
  7. Inheritance is a toy. Averaging parental coefficients with noise is not genetics. It gives the house lineages you can follow, which is what we wanted, and it is not a claim about heredity.
  8. The day length is a show decision. Six minutes per day has nothing to do with fly biology. It exists so a visitor sees a night.
  9. Nothing is deployed. No contract, no voting, no prize pool. The vote bars are explicitly a preview of the mechanic.