import { proxyHost } from "./cluster-bus.js";
import qrcode from "./vendor/qrcode-generator.mjs";
import { highlightAll } from "./highlight.js";
import {
joinMesh, roomCode, turnServers, iceProbe, iceVerdict, DEFAULT_P2P_MODULE,
} from "./cluster-p2p.js";
const $ = (id) => document.getElementById(id);
const fmt = (v, n = 2) => (Number.isFinite(v) ? v.toFixed(n) : "—");
const params = new URLSearchParams(location.search);
const worker = new Worker("./cluster-worker.js", { type: "module" });
const history = [];
const MAX_POINTS = 240;
const note = (text, cls = "warn") => {
$("fault").textContent = text;
$("fault").className = "v " + cls;
};
worker.onmessage = (ev) => {
const s = ev.data;
if (s.t === "log") return note(s.text);
if (s.t !== "state") return;
$("rank-id").textContent = s.id;
$("role").textContent = s.leader ? "leader (drives the step)" : "member";
$("role").className = "v " + (s.leader ? "ok" : "");
$("state").textContent = !s.ready ? "syncing parameters…" : s.running ? "training" : "idle";
$("state").className = "v " + (s.ready ? (s.running ? "ok" : "warn") : "warn");
$("backend").textContent = s.backend ?? "—";
$("model").textContent = `${s.model} · batch ${s.batch} · ${s.bucket.toLocaleString()} params`;
$("cohort").textContent = `${s.cohort} rank${s.cohort === 1 ? "" : "s"}`;
$("peers").textContent = s.peers.length
? s.peers.map((p) => `${p.id} (${p.backend})`).join(", ")
: "(alone)";
$("step").textContent = s.step.toLocaleString();
$("loss").textContent = fmt(s.loss, 4);
$("steps-sec").textContent = `${fmt(s.stepsPerSec, 1)} steps/s`;
$("samples-sec").textContent = `${fmt(s.samplesPerSec, 0)} samples/s (cluster)`;
$("comm").textContent =
`${fmt(s.bytesPerSec / 1e6, 2)} MB/s` +
(s.precision && s.precision !== "f32" ? ` (${s.precision} wire)` : "");
if (s.policy) {
const unanimous = s.policy.quorum >= s.policy.n;
$("policy").textContent =
`quorum ${s.policy.quorum}/${s.policy.n}` +
`${unanimous ? " (unanimous)" : ""} · beat ${s.policy.beat}ms`;
}
if (s.fault) note(s.fault, "err");
if (Number.isFinite(s.loss)) {
history.push(s.loss);
if (history.length > MAX_POINTS) history.shift();
drawLoss();
}
};
function drawLoss() {
const c = $("curve");
const ctx = c.getContext("2d");
const w = c.width, h = c.height;
ctx.clearRect(0, 0, w, h);
if (history.length < 2) return;
const lo = Math.min(...history), hi = Math.max(...history);
const span = hi - lo || 1;
const x = (i) => (i / (history.length - 1)) * (w - 2) + 1;
const y = (v) => h - 1 - ((v - lo) / span) * (h - 2);
ctx.strokeStyle = "#0a7";
ctx.lineWidth = 1.5;
ctx.beginPath();
history.forEach((v, i) => (i ? ctx.lineTo(x(i), y(v)) : ctx.moveTo(x(i), y(v))));
ctx.stroke();
ctx.fillStyle = "#666";
ctx.font = "11px ui-monospace, monospace";
ctx.fillText(hi.toFixed(3), 4, 12);
ctx.fillText(lo.toFixed(3), 4, h - 4);
}
function renderQr(text, canvasId = "qr", box = 180) {
const qr = qrcode(0, "M");
qr.addData(text);
qr.make();
const modules = qr.getModuleCount();
const quiet = 4;
const canvas = $(canvasId);
const scale = Math.max(2, Math.floor(box / (modules + quiet * 2)));
const size = (modules + quiet * 2) * scale;
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = "#000";
for (let row = 0; row < modules; row++) {
for (let col = 0; col < modules; col++) {
if (qr.isDark(row, col)) {
ctx.fillRect((col + quiet) * scale, (row + quiet) * scale, scale, scale);
}
}
}
if (canvasId === "qr") $("qr-row").hidden = false;
}
async function renderIce(mesh) {
const conns = Object.entries(mesh.connections());
if (!conns.length) {
$("ice").textContent = "no peer connections yet";
return;
}
const rows = [];
for (const [peerId, pc] of conns) {
const gathered = new Map();
let remoteCandidates = 0;
let path = "?";
try {
const stats = await pc.getStats();
const byId = new Map();
stats.forEach((s) => byId.set(s.id, s));
stats.forEach((s) => {
if (s.type === "local-candidate") {
gathered.set(s.candidateType, (gathered.get(s.candidateType) ?? 0) + 1);
}
if (s.type === "remote-candidate") remoteCandidates += 1;
if (s.type === "candidate-pair" && (s.selected || s.nominated)) {
const local = byId.get(s.localCandidateId)?.candidateType ?? "?";
const remote = byId.get(s.remoteCandidateId)?.candidateType ?? "?";
path = `${local}→${remote}`;
}
});
} catch {
}
const kinds = [...gathered].map(([k, n]) => `${k}×${n}`).join(" ") || "none";
rows.push(
`${peerId.slice(0, 8)} ${pc.connectionState} · ${path} · gathered ${kinds}` +
` · ${remoteCandidates} remote`,
);
}
$("ice").textContent = rows.join(" | ");
}
const SEED = 42;
const room = params.get("room");
function applyUrlOverrides() {
const set = (id, key) => {
const v = params.get(key);
if (v === null) return;
const el = $(id);
el.value = v;
if (el.value !== v) {
note(`${key}=${v} is not a valid option; using ${el.value || "the default"}`);
}
};
set("model-select", "model");
set("backend-select", "backend");
set("batch", "batch");
{
const n = Number($("batch").value);
if (Number.isFinite(n) && n > 0) {
$("batch-exp").value = String(Math.round(Math.log2(n)));
$("batch-shown").textContent = n.toLocaleString();
}
}
set("precision", "precision");
set("lr", "lr");
$("lr-shown").textContent = $("lr").value;
}
let started = false;
let meshPeers = 0;
let icePreflight = null;
const peerFailures = new Set();
async function begin() {
started = true;
let transport = "local";
if (room) {
const turn = turnServers(params.get("turn"));
iceProbe({
turn,
...(params.get("ice")
? { iceServers: [{ urls: params.get("ice").split(",").filter(Boolean) }] }
: {}),
})
.then((probe) => {
icePreflight = iceVerdict(probe); $("ice-preflight").textContent = icePreflight.text;
$("ice-preflight").className = "v " + (icePreflight.ok ? "ok" : "warn");
})
.catch(() => {});
$("room").textContent = room;
$("join-url").textContent = location.href;
try {
renderQr(location.href);
} catch (e) {
note(`QR unavailable (${e?.message ?? e}) — use the join URL above`);
}
let meshSend = null;
const host = proxyHost(worker, (buf) => meshSend?.(buf), {
onDrop: (text) => note(`mesh queue: ${text}`),
});
try {
const mesh = await joinMesh({
room,
moduleUrl: params.get("p2p") ?? DEFAULT_P2P_MODULE,
relayUrls: params.get("relays")?.split(",").filter(Boolean),
...(params.get("ice")
? { iceServers: [{ urls: params.get("ice").split(",").filter(Boolean) }] }
: {}),
turn,
onData: (buf) => host.deliver(buf),
onPeers: (n) => {
meshPeers = n;
$("transport").textContent = `WebRTC mesh · room ${room} · ${n} peer${n === 1 ? "" : "s"}`;
},
onError: (text) => note(`mesh: ${text}`, "err"),
onPeerError: (peerId, why) => {
const short = String(peerId).slice(0, 8);
if (peerFailures.has(short)) return;
peerFailures.add(short);
const live = meshPeers;
let tail;
if (live > 0) {
tail = `continuing with ${live} connected peer${live === 1 ? "" : "s"}`;
} else if (icePreflight && !icePreflight.ok) {
tail = `and ICE preflight already failed here — ${icePreflight.text}`;
} else {
tail =
"no peer has connected yet. Either that peer is gone or suspended" +
" (a closed tab stays advertised on the relay for a while), or both" +
" ends are behind symmetric NAT / client isolation — the ICE line" +
" below says which.";
}
note(`peer ${short} unreachable (${why}) — ${tail}`);
},
});
meshSend = mesh.send;
transport = "mesh";
renderIce(mesh);
setInterval(() => renderIce(mesh), 3000);
window.__rlxCluster = { room, mesh };
$("transport").textContent = `WebRTC mesh · room ${room} · 0 peers`;
$("transport").className = "v ok";
} catch (e) {
const why = String(e?.message ?? e);
const hint = /TURN|could not connect to peer/i.test(why)
? " — signalling worked but no direct path exists between these networks;" +
" pass ?turn=turn:host:3478|user|cred (it relays every byte) or put both" +
" devices on one network"
: " — this browser's tabs only";
note(`mesh unavailable (${why})${hint}`, "err");
$("transport").textContent = "BroadcastChannel (this browser only)";
$("transport").className = "v warn";
}
} else {
$("transport").textContent = "BroadcastChannel (this browser only)";
$("transport").className = "v warn";
}
worker.postMessage({
t: "start",
model: $("model-select").value,
backend: $("backend-select").value,
batch: Number($("batch").value),
lr: Number($("lr").value),
seed: SEED,
transport,
precision: $("precision").value,
});
$("start").disabled = true;
$("stop").disabled = false;
}
$("start").onclick = () => {
if (!started) {
begin();
return;
}
const next = new URL(location.href);
next.searchParams.set("model", $("model-select").value);
next.searchParams.set("backend", $("backend-select").value);
next.searchParams.set("batch", $("batch").value);
next.searchParams.set("precision", $("precision").value);
next.searchParams.set("lr", $("lr").value);
if (room) next.searchParams.set("room", room);
location.href = next.href;
};
$("stop").onclick = () => {
worker.postMessage({ t: "stop" });
$("start").disabled = false;
$("stop").disabled = true;
};
$("batch-exp").oninput = () => {
const n = 2 ** Number($("batch-exp").value);
$("batch").value = String(n);
$("batch-shown").textContent = n.toLocaleString();
};
$("lr").oninput = () => {
$("lr-shown").textContent = $("lr").value;
worker.postMessage({ t: "lr", lr: Number($("lr").value) });
};
$("spawn").onclick = () => window.open(location.href, "_blank");
$("open-room").onclick = () => {
const next = new URL(location.href);
next.searchParams.set("room", room ?? roomCode());
location.href = next.href;
};
{
const masthead = document.querySelector(".masthead");
const clone = masthead.cloneNode(true);
clone.querySelectorAll("canvas").forEach((c) => {
c.id += "-foot";
});
clone.classList.add("colophon");
$("colophon").replaceWith(clone);
}
const LINKS = [
["qr-repo", "https://github.com/MIT-RLX/rlx"],
["qr-models", "https://github.com/MIT-RLX/rlx-models"],
["qr-paper", "https://arxiv.org/abs/2609.37916"],
];
for (const [id, url] of LINKS) {
for (const target of [id, `${id}-foot`]) {
try {
renderQr(url, target, 104);
} catch (e) {
note(`QR for ${target} unavailable (${e?.message ?? e})`);
}
}
}
highlightAll();
applyUrlOverrides();
begin();