robot-folding / app /src /content /embeds /folding /rtc-explainer.html
pepijn223's picture
pepijn223 HF Staff
Improve DAgger explainer, add conclusion and expand references
f0f3d44 unverified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
:root { --bg: transparent; --text: #e8eaf0; --subtext: #8b8fa8; }
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: var(--bg); font-family: system-ui, sans-serif; color: var(--text); }
.controls {
display: flex; align-items: center; gap: 10px; justify-content: center;
margin: 0 0 4px; font-size: 14px; flex-wrap: wrap;
}
.mode-toggle {
display: flex; border-radius: 6px; overflow: hidden; border: 1px solid #333;
}
.mode-btn {
padding: 6px 16px; font-size: 13px; font-weight: 600; cursor: pointer;
border: none; background: #1a1d2e; color: #666;
transition: all .2s; font-family: inherit;
}
.mode-btn.active { background: #252540; color: #e8eaf0; }
.mode-btn:hover:not(.active) { background: #1f2238; color: #999; }
.play-btn {
width: 26px; height: 26px; border-radius: 50%; border: 1px solid #444;
background: #1a1d2e; color: #e8eaf0; cursor: pointer; font-size: 11px;
display: flex; align-items: center; justify-content: center; font-family: inherit;
transition: background .2s;
}
.play-btn:hover { background: #252540; }
.time-display { font-weight: 700; font-size: 14px; min-width: 70px; text-align: right; }
.legend-row {
display: flex; gap: 14px; justify-content: center; font-size: 12px;
color: #8b8fa8; margin-top: 4px; flex-wrap: wrap;
}
.legend-row span { display: flex; align-items: center; gap: 4px; }
.legend-swatch { width: 12px; height: 8px; border-radius: 2px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="controls">
<div class="mode-toggle">
<button class="mode-btn active" data-mode="sync">Synchronous</button>
<button class="mode-btn" data-mode="rtc">RTC</button>
</div>
<button class="play-btn" id="play-btn"></button>
<input type="range" id="speed-slider" min="0.5" max="3" step="0.5" value="1" style="width:60px;accent-color:#6366f1;cursor:pointer;" title="Playback speed"/>
<span class="time-display" id="time-display"></span>
</div>
<svg id="rtc-svg"></svg>
<div class="legend-row" id="legend-row">
<span><span class="legend-swatch" style="background:#7c7fff"></span>Inference</span>
<span><span class="legend-swatch" style="background:#22d3ee"></span>Executing</span>
<span id="legend-idle"><span class="legend-swatch" style="background:transparent;border:1px dashed #555"></span>Idle</span>
</div>
<script>
function _initRTC() {
const CHUNK = 10, INF = 3, N_CHUNKS = 4;
function buildSync() {
const inf = [], exec = [], idle = [];
let t = 0;
for (let i = 0; i < N_CHUNKS; i++) {
inf.push({ s: t, e: t + INF, i: i });
idle.push({ s: t, e: t + INF });
t += INF;
exec.push({ s: t, e: t + CHUNK, i: i, lock: 0 });
t += CHUNK;
}
return { inf, exec, idle, T: t };
}
function buildRTC() {
const inf = [], exec = [], idle = [];
inf.push({ s: 0, e: INF, i: 0 });
idle.push({ s: 0, e: INF });
let t = INF;
for (let i = 0; i < N_CHUNKS; i++) {
exec.push({ s: t, e: t + CHUNK, i: i, lock: i > 0 ? INF : 0 });
if (i + 1 < N_CHUNKS) inf.push({ s: t, e: t + INF, i: i + 1 });
t += CHUNK - (i + 1 < N_CHUNKS ? INF : 0);
}
return { inf, exec, idle, T: exec[exec.length - 1].e };
}
const SYNC = buildSync(), RTC_ = buildRTC();
let mode = "sync", playing = false, playT = 0, speed = 1, animId = null, lastFrame = null;
const container = document.getElementById("rtc-svg").parentElement;
const svg = d3.select("#rtc-svg");
const playBtn = document.getElementById("play-btn");
const speedSlider = document.getElementById("speed-slider");
const timeDisp = document.getElementById("time-display");
const legendIdle = document.getElementById("legend-idle");
const legendRow = document.getElementById("legend-row");
const infPalette = ["#7c7fff", "#9590ff", "#7c7fff", "#9590ff"];
const execPalette = ["#22d3ee", "#14b8a6", "#06b6d4", "#0ea5e9"];
const lockColor = "#0c5e6f";
function data() { return mode === "sync" ? SYNC : RTC_; }
function render() {
svg.selectAll("*").remove();
const d = data();
const W = container.clientWidth || 600;
const ml = 72, mr = 12, mt = 6, mb = 24;
const laneH = 32, gap = 20;
const w = W - ml - mr;
const H = mt + laneH * 2 + gap + mb;
svg.attr("width", W).attr("height", H).attr("viewBox", `0 0 ${W} ${H}`);
const g = svg.append("g").attr("transform", `translate(${ml},${mt})`);
const maxT = Math.max(SYNC.T, RTC_.T);
const x = d3.scaleLinear().domain([0, maxT]).range([0, w]);
const iy = 0, ey = laneH + gap;
g.append("rect").attr("x", 0).attr("y", iy).attr("width", w).attr("height", laneH)
.attr("fill", "#ffffff").attr("opacity", 0.02).attr("rx", 4);
g.append("rect").attr("x", 0).attr("y", ey).attr("width", w).attr("height", laneH)
.attr("fill", "#ffffff").attr("opacity", 0.02).attr("rx", 4);
g.append("text").attr("x", -10).attr("y", iy + laneH / 2)
.attr("text-anchor", "end").attr("dominant-baseline", "middle")
.attr("fill", "#8b8fa8").attr("font-size", 12).attr("font-weight", 500).text("Predict");
g.append("text").attr("x", -10).attr("y", ey + laneH / 2)
.attr("text-anchor", "end").attr("dominant-baseline", "middle")
.attr("fill", "#8b8fa8").attr("font-size", 12).attr("font-weight", 500).text("Execute");
d.idle.forEach(id => {
const bx = x(id.s), bw = x(id.e) - x(id.s);
g.append("rect").attr("x", bx + 1).attr("y", ey + 3).attr("width", Math.max(0, bw - 2)).attr("height", laneH - 6)
.attr("fill", "none").attr("stroke", "#444").attr("stroke-width", 1)
.attr("stroke-dasharray", "4,3").attr("rx", 3);
});
d.inf.forEach(b => {
const bx = x(b.s), bw = x(b.e) - x(b.s);
const col = infPalette[b.i % infPalette.length];
g.append("rect").attr("x", bx).attr("y", iy + 3).attr("width", bw).attr("height", laneH - 6)
.attr("fill", col).attr("opacity", 0.9).attr("rx", 4);
const connX = bx + bw / 2;
const execBlock = d.exec.find(e => e.i === b.i);
if (execBlock) {
const targetX = x(execBlock.s) + (x(execBlock.e) - x(execBlock.s)) / 2;
g.append("line")
.attr("x1", connX).attr("y1", iy + laneH - 2)
.attr("x2", targetX).attr("y2", ey + 2)
.attr("stroke", col).attr("stroke-width", 1)
.attr("stroke-dasharray", "2,3").attr("opacity", 0.3);
}
});
d.exec.forEach(b => {
const bx = x(b.s), bw = x(b.e) - x(b.s);
const col = execPalette[b.i % execPalette.length];
if (b.lock > 0) {
const lw = x(b.s + b.lock) - bx;
g.append("rect").attr("x", bx).attr("y", ey + 3).attr("width", bw).attr("height", laneH - 6)
.attr("fill", col).attr("opacity", 0.85).attr("rx", 4);
g.append("rect").attr("x", bx).attr("y", ey + 3).attr("width", lw).attr("height", laneH - 6)
.attr("fill", lockColor).attr("opacity", 0.95).attr("rx", 4);
for (let sx = bx + 3; sx < bx + lw - 1; sx += 5) {
g.append("line").attr("x1", sx).attr("y1", ey + 3)
.attr("x2", sx + (laneH - 6) * 0.4).attr("y2", ey + laneH - 3)
.attr("stroke", "#1a8a9e").attr("stroke-width", 0.8).attr("opacity", 0.5);
}
g.append("line").attr("x1", bx + lw).attr("y1", ey + 5)
.attr("x2", bx + lw).attr("y2", ey + laneH - 5)
.attr("stroke", "#fff").attr("stroke-width", 0.5).attr("opacity", 0.3);
} else {
g.append("rect").attr("x", bx).attr("y", ey + 3).attr("width", bw).attr("height", laneH - 6)
.attr("fill", col).attr("opacity", 0.85).attr("rx", 4);
}
if (bw > 30) {
g.append("text").attr("x", bx + bw / 2).attr("y", ey + laneH / 2)
.attr("text-anchor", "middle").attr("dominant-baseline", "middle")
.attr("fill", "#fff").attr("font-size", 12).attr("font-weight", 600)
.attr("opacity", 0.9).text("C" + (b.i + 1));
}
});
const ticks = [];
const step = maxT > 30 ? 10 : 5;
for (let t = 0; t <= maxT; t += step) ticks.push(t);
ticks.forEach(t => {
const tx = x(t);
g.append("line").attr("x1", tx).attr("y1", ey + laneH + 2).attr("x2", tx).attr("y2", ey + laneH + 5)
.attr("stroke", "#555").attr("stroke-width", 0.5);
g.append("text").attr("x", tx).attr("y", ey + laneH + 14)
.attr("text-anchor", "middle").attr("fill", "#666").attr("font-size", 11)
.text(Math.round(t * 33) + "ms");
});
const playhead = g.append("line")
.attr("x1", x(playT)).attr("x2", x(playT))
.attr("y1", -2).attr("y2", ey + laneH + 2)
.attr("stroke", "#f87171").attr("stroke-width", 1.5).attr("opacity", playing || playT > 0 ? 0.9 : 0);
g.append("circle").attr("cx", x(playT)).attr("cy", -2).attr("r", 3)
.attr("fill", "#f87171").attr("opacity", playing || playT > 0 ? 0.9 : 0).attr("class", "ph-dot");
const totalMs = Math.round(d.T * 33);
const syncMs = Math.round(SYNC.T * 33);
if (mode === "rtc") {
const pct = Math.round((syncMs - totalMs) / syncMs * 100);
timeDisp.innerHTML = `<span style="color:#10b981">${totalMs}ms</span> <span style="color:#666">(${pct}% faster)</span>`;
} else {
timeDisp.innerHTML = `<span style="color:#666">${totalMs}ms</span>`;
}
if (mode === "rtc") {
legendIdle.style.display = "none";
if (!document.getElementById("legend-locked")) {
const sp = document.createElement("span");
sp.id = "legend-locked";
sp.innerHTML = `<span class="legend-swatch" style="background:${lockColor}"></span>Committed`;
legendRow.appendChild(sp);
}
} else {
legendIdle.style.display = "";
const lk = document.getElementById("legend-locked");
if (lk) lk.remove();
}
window._ph = { line: playhead, dot: g.select(".ph-dot"), x };
}
function animate(ts) {
if (!playing) return;
if (!lastFrame) lastFrame = ts;
const dt = (ts - lastFrame) / 1000;
lastFrame = ts;
playT += dt * speed * 8;
if (playT > data().T) { playT = 0; lastFrame = null; }
if (window._ph) {
const px = window._ph.x(playT);
window._ph.line.attr("x1", px).attr("x2", px).attr("opacity", 0.9);
window._ph.dot.attr("cx", px).attr("opacity", 0.9);
}
animId = requestAnimationFrame(animate);
}
playBtn.addEventListener("click", () => {
playing = !playing;
playBtn.textContent = playing ? "⏸" : "▶";
if (playing) { lastFrame = null; animId = requestAnimationFrame(animate); }
else cancelAnimationFrame(animId);
});
speedSlider.addEventListener("input", () => {
speed = parseFloat(speedSlider.value);
});
document.querySelectorAll(".mode-btn").forEach(btn => {
btn.addEventListener("click", () => {
document.querySelectorAll(".mode-btn").forEach(b => b.classList.remove("active"));
btn.classList.add("active");
mode = btn.dataset.mode;
playT = 0;
render();
});
});
render();
window.addEventListener("resize", render);
}
if (typeof d3 !== "undefined") { _initRTC(); }
else { var s = document.createElement("script"); s.src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"; s.onload=_initRTC; document.head.appendChild(s); }
</script>
</body>
</html>