fly-sim / live.html
epoch14's picture
Upload folder using huggingface_hub
0cd3460 verified
Raw History Blame Contribute Delete
6.04 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FlySim LIVE — feed the flies</title>
<style>
:root { color-scheme: dark; }
* { box-sizing: border-box; }
body { margin: 0; font: 16px/1.6 system-ui, sans-serif; background: #0d1117; color: #e6edf3; }
main { max-width: 1080px; margin: 0 auto; padding: 32px 20px 80px; }
h1 { font-size: 1.5rem; margin: 0 0 6px; display: flex; align-items: center; gap: 12px; }
.badge { background: #da3633; color: #fff; font-weight: 700; font-size: .72rem;
padding: 3px 12px; border-radius: 999px; letter-spacing: .1em; }
p.sub { color: #8b949e; margin: 0 0 20px; max-width: 820px; }
a { color: #58a6ff; text-decoration: none; } a:hover { text-decoration: underline; }
#framebox { position: relative; border: 1px solid #30363d; border-radius: 10px;
overflow: hidden; background: #000; aspect-ratio: 16/9.6; }
#framebox iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
#placeholder { position: absolute; inset: 0; display: flex; flex-direction: column;
gap: 14px; align-items: center; justify-content: center; color: #8b949e;
text-align: center; padding: 20px; }
#placeholder b { color: #e6edf3; }
.urlrow { display: flex; gap: 8px; margin: 14px 0; flex-wrap: wrap; }
input[type=text] { flex: 1; min-width: 260px; background: #161b22; color: #e6edf3;
border: 1px solid #30363d; border-radius: 8px; padding: 9px 12px;
font-size: .9rem; }
button { background: #238636; color: #fff; border: 1px solid #2ea043; border-radius: 8px;
padding: 9px 18px; cursor: pointer; font-size: .9rem; font-weight: 600; }
button.sec { background: #21262d; color: #e6edf3; border-color: #30363d; font-weight: 400; }
button:hover { filter: brightness(1.15); }
.how { background: #161b22; border: 1px solid #21262d; border-radius: 10px;
padding: 14px 18px; margin-top: 18px; }
.how h3 { margin: 0 0 8px; font-size: .85rem; color: #8b949e; text-transform: uppercase;
letter-spacing: .06em; }
code { background: #0d1117; border: 1px solid #21262d; padding: 2px 8px; border-radius: 6px;
font-size: .85em; display: inline-block; margin: 2px 0; }
.note { color: #8b949e; font-size: .88rem; }
video { width: 100%; border-radius: 10px; background: #000; }
</style>
</head>
<body>
<main>
<h1><span class="badge">LIVE</span> Feed the flies — the real simulator, running now</h1>
<p class="sub">Two actual MuJoCo <i>Drosophila</i> walkers, streamed frame-by-frame from a
running simulation. <b>Click the arena: the food patch moves there</b> and both flies
re-orient with the M4-verified antennal chemotaxis, walk over, and feed. Energy drains,
they get hungry again. You are the ecosystem's only food supply.</p>
<div class="urlrow">
<input type="text" id="url" placeholder="paste a live sim URL — e.g. https://<job-id>--7860.hf.jobs" spellcheck="false">
<button id="go">Watch</button>
<button class="sec" id="stop">Stop</button>
</div>
<div id="framebox">
<div id="placeholder">
<b>No live sim connected.</b>
<span>Launch one below (about a minute to boot), paste the URL above, then click the arena to feed the flies.</span>
<video controls muted loop preload="metadata" src="colony.mp4" style="max-width:520px"></video>
<span class="note">…or watch the recorded colony while it boots.</span>
</div>
</div>
<div class="how">
<h3>Launch a live sim</h3>
<p class="note" style="margin-top:0"><b>Option A — your own machine (fastest, fully interactive):</b><br>
<code>docker run --rm -p 7860:7860 -v $PWD/data:/app/data -e MUJOCO_GL=egl fly-sim:space python -m flysim.live_colony</code><br>
then use <code>http://localhost:7860</code> above.</p>
<p class="note"><b>Option B — Hugging Face compute (slow-motion, shareable):</b><br>
<code>python -m flysim.jobs --live-colony --timeout 3600</code><br>
prints <code>https://&lt;job-id&gt;--7860.hf.jobs</code> — paste it above. Note: HF job URLs
require the viewer to be logged in to a Hugging Face account.</p>
</div>
<p class="note" style="margin-top:18px">What am I looking at? Real physics at 10 kHz (slowed
to watchable speed), the same walkers, energy law (0.02/s drain, 0.25/s feeding) and odor
sensing verified in milestones M1–M4. The flies cannot see the food — they smell the
gradient across their two antennae and pulse-turn until aligned. Sated flies loiter in a
slow orbit near the food; hunger pulls them back. Starvation is real: clicks farther than
~35&nbsp;mm may cost a fly its life — the <b>reset colony</b> button in the viewer respawns
them. Courtship encounters are logged when they cross paths.
Back to the <a href="index.html">dashboard</a> · <a href="player.html">trajectory player</a>.</p>
</main>
<script>
const box = document.getElementById("framebox"), ph = document.getElementById("placeholder"),
urlIn = document.getElementById("url");
let saved = localStorage.getItem("flysim-live-url") || "";
urlIn.value = saved;
function connect(u) {
u = u.trim().replace(/\/+$/, "");
if (!u) return;
if (!/^https?:\/\//.test(u)) u = "https://" + u;
localStorage.setItem("flysim-live-url", u);
urlIn.value = u;
ph.style.display = "none";
const old = document.getElementById("liveframe");
if (old) old.remove();
const f = document.createElement("iframe");
f.id = "liveframe"; f.src = u; f.allow = "fullscreen";
box.appendChild(f);
}
document.getElementById("go").onclick = () => connect(urlIn.value);
urlIn.addEventListener("keydown", e => { if (e.key === "Enter") connect(urlIn.value); });
document.getElementById("stop").onclick = () => {
const old = document.getElementById("liveframe");
if (old) old.remove();
ph.style.display = "";
};
if (saved) connect(saved);
</script>
</body>
</html>