File size: 6,040 Bytes
28e44b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0cd3460
28e44b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0cd3460
 
 
 
 
28e44b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<!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>