BenchLabs-Leaderboard / index.html
astrvolt's picture
Update index.html
afca0ac verified
Raw
History Blame Contribute Delete
20.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bench Labs Leaderboard</title>
<meta name="description" content="Model performance across Bench Labs' benchmark suite — dual-mode 7-2026 tiers, legacy 6-2026 runs, and text-to-image FID scores.">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Newsreader:ital,opsz,wght@0,6..72,400;1,6..72,300;1,6..72,400&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
serif: ['Newsreader', 'serif'],
}
}
}
}
</script>
<link rel="stylesheet" href="style.css">
</head>
<body class="antialiased min-h-screen overflow-y-auto selection:bg-[rgba(124,156,255,0.35)]">
<div class="space-bg"></div>
<canvas class="stars" id="stars"></canvas>
<div class="relative z-10 min-h-screen flex flex-col items-center px-6 py-14 md:py-16">
<!-- Logo -->
<img src="benchlabs.svg" alt="Bench-labs" class="h-14 md:h-16 w-auto mb-6 fade d1"
style="filter: drop-shadow(0 0 18px rgba(124,156,255,0.35));" />
<!-- Hero -->
<header class="hero text-center max-w-3xl w-full fade d2">
<h1 class="font-serif italic text-[2.2rem] md:text-[2.7rem] leading-[0.95] tracking-tight">Leaderboard</h1>
<p class="subtitle mt-3 text-sm md:text-[14.5px] leading-6 font-light max-w-[56ch] mx-auto">
<span id="rotating-headline" class="rotating-headline text-[var(--ink)] font-normal">Every evaluated model, every benchmark, one table.</span>
</p>
</header>
<!-- Stats -->
<section class="stat-row max-w-5xl w-full mt-8 fade d3" id="stat-row" aria-label="Summary statistics"></section>
<!-- Controls -->
<section class="controls max-w-5xl w-full fade d3">
<div class="chip-row" id="gen-chips" role="tablist" aria-label="Benchmark generation"></div>
<div class="controls-right">
<label class="search-pill">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="6"/><line x1="20" y1="20" x2="15.5" y2="15.5"/></svg>
<input type="search" id="model-search" placeholder="Search models or orgs…" aria-label="Search models">
</label>
<div class="sort-pill" id="sort-pill">
<span class="sort-label">Sort</span>
<button type="button" class="sort-trigger" id="sort-trigger" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="sort-trigger-label">
<span id="sort-trigger-label">Avg ↓</span>
<svg class="sort-chevron" width="10" height="6" viewBox="0 0 10 6" fill="none"><path d="M1 1L5 5L9 1" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<ul class="sort-menu" id="sort-menu" role="listbox" aria-labelledby="sort-trigger-label" tabindex="-1" hidden>
<li role="option" data-value="avg-desc" aria-selected="true" class="selected">Avg ↓</li>
<li role="option" data-value="avg-asc" aria-selected="false">Avg ↑</li>
<li role="option" data-value="params-asc" aria-selected="false">Params ↑</li>
<li role="option" data-value="params-desc" aria-selected="false">Params ↓</li>
<li role="option" data-value="name-asc" aria-selected="false">Name A–Z</li>
</ul>
<select id="sort-select" aria-hidden="true" tabindex="-1">
<option value="avg-desc" selected>Avg ↓</option>
<option value="avg-asc">Avg ↑</option>
<option value="params-asc">Params ↑</option>
<option value="params-desc">Params ↓</option>
<option value="name-asc">Name A–Z</option>
</select>
</div>
</div>
</section>
<!-- Table card -->
<section class="table-card max-w-5xl w-full fade d4">
<div class="table-scroll">
<table id="lb">
<thead id="lb-head"></thead>
<tbody id="lb-body"></tbody>
</table>
</div>
<div id="model-cards" aria-label="Models (mobile view)"></div>
<div id="empty-state" hidden>
<p class="empty-title">No models evaluated on this generation yet.</p>
<p class="empty-sub">The 7-2026 tiers are brand new — run
<code>python script.py --model &lt;your-model&gt;</code> and open a PR with the
<code>leaderboard.json</code> it prints. Or view the
<button type="button" class="linklike" id="show-legacy">6-2026 runs</button>.</p>
</div>
</section>
<!-- Text-to-image mini leaderboard (independent track: FID / MS-COCO) -->
<section id="t2i-section" class="max-w-5xl w-full fade d4" hidden>
<div class="section-heading">
<h2>Text-to-image</h2>
<span class="section-note">FID on MS-COCO — lower is better</span>
</div>
<div class="table-card">
<div class="table-scroll">
<table id="t2i-table">
<thead id="t2i-head"></thead>
<tbody id="t2i-body"></tbody>
</table>
</div>
<div id="t2i-cards" aria-label="Text-to-image models (mobile view)"></div>
<div id="t2i-empty" hidden>
<p>No text-to-image models evaluated yet.</p>
</div>
</div>
</section>
<!-- Howto -->
<section class="howto max-w-5xl w-full fade d5">
<details id="howto-details">
<summary>Entry schema &amp; how to submit</summary>
<div class="howto-content">
<div class="howto-content-inner">
<p>Run the universal script — it fetches the live leaderboard and merges your run into it, writing an upload-ready <code>models.json</code> next to the results:</p>
<pre><code>python script.py --model your-org/your-model
# then upload benchlabs_results/&lt;model&gt;/models.json to this Space (PR welcome).
# Same model id: only the tiers you just ran are replaced; everything else kept.
# 7-2026 runs report both modes:
# metrics.generative exact_match · hybrid_score (alias-aware, per-item routing)
# metrics.loglikelihood acc · acc_norm · soft_score · soft_score_norm
# model_revision + script_sha256 pin exactly what was evaluated.
#
# Text-to-image entries (FID / MS-COCO) live in a separate top-level
# "t2i_models" array in models.json — see add_t2i_schema.py for the entry shape.</code></pre>
</div>
</div>
</details>
</section>
<!-- Footer -->
<footer class="foot max-w-5xl w-full fade d6">
<div class="section-divider mb-6"><div class="line"></div><span class="text-[10px] tracking-widest uppercase text-[var(--muted-2)]">bench-labs</span><div class="line"></div></div>
<p>Bench Labs — simple, reliable, open sourced.
Data: <a href="models.json">models.json</a> ·
Datasets: <a href="https://huggingface.co/bench-labs" target="_blank" rel="noopener">huggingface.co/bench-labs</a>
</p>
</footer>
</div>
<!-- Starfield + rotating headline scripts (same architecture as Members) -->
<script>
(()=>{
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const isMobile = window.matchMedia("(max-width: 767px)").matches || navigator.userAgentData?.mobile === true || /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
const canvas = document.getElementById("stars");
if (!canvas) return;
const ctx = canvas.getContext("2d");
let dpr = Math.min(window.devicePixelRatio || 1, 2);
let W = 0, H = 0;
function resize() {
W = window.innerWidth; H = window.innerHeight;
canvas.width = Math.floor(W * dpr);
canvas.height = Math.floor(H * dpr);
canvas.style.width = W + "px";
canvas.style.height = H + "px";
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
resize();
let resizeTimer;
window.addEventListener("resize", ()=>{
clearTimeout(resizeTimer);
resizeTimer = setTimeout(resize, 150);
});
// ---------- Background stars: tiny radius, slow drift, subtle twinkle ----------
const STAR_COUNT = isMobile ? 90 : 190;
const stars = [];
for (let i=0;i<STAR_COUNT;i++) {
const big = Math.random() < 0.12;
stars.push({
x: Math.random()*W,
y: Math.random()*H,
r: big ? (0.8 + Math.random()*0.2) : (0.5 + Math.random()*0.5), // 0.5px - 1px radius
baseOp: 0.25 + Math.random()*0.35,
twinkleAmp: 0.25 + Math.random()*0.45,
twinkleSpeed: 0.4 + Math.random()*0.8, // rad/sec-ish
phase: Math.random()*Math.PI*2,
vx: (Math.random()-0.5) * 0.012, // slow drift, px/frame-ish (scaled by dt)
vy: (Math.random()-0.5) * 0.012 + 0.004, // gentle downward bias
});
}
// ---------- Shooting stars: dense particle trail instead of a single dash ----------
function spawnShootingStar(delay=0) {
const angle = (18 + Math.random()*26) * (Math.PI/180); // 18°-44° downward-right
const speed = 620 + Math.random()*420; // px/sec, varied
const startX = -0.15*W - Math.random()*0.1*W;
const startY = Math.random()*H*0.55;
return {
x: startX, y: startY,
vx: Math.cos(angle)*speed,
vy: Math.sin(angle)*speed,
life: 0,
maxLife: 0.9 + Math.random()*0.5, // seconds visible
delay,
len: 70 + Math.random()*90, // trail length in px
thickness: 1 + Math.random()*1.1,
trail: [], // recent positions for dense particle trail
active: false,
done: false,
};
}
const shootingStars = [];
if (!isMobile && !prefersReducedMotion) {
const SHOOTER_COUNT = 3;
for (let i=0;i<SHOOTER_COUNT;i++) {
shootingStars.push(spawnShootingStar(i*3.5 + Math.random()*4));
}
}
let lastTime = performance.now();
let elapsed = 0;
function drawStars(dt) {
for (const s of stars) {
s.x += s.vx * dt * 60;
s.y += s.vy * dt * 60;
// wrap around edges so drift is seamless/continuous
if (s.x < -2) s.x = W + 2; else if (s.x > W + 2) s.x = -2;
if (s.y < -2) s.y = H + 2; else if (s.y > H + 2) s.y = -2;
const tw = prefersReducedMotion ? 0 : Math.sin(elapsed*s.twinkleSpeed + s.phase) * s.twinkleAmp;
const op = Math.max(0.04, Math.min(1, s.baseOp + tw));
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI*2);
ctx.fillStyle = `rgba(255,255,255,${op.toFixed(3)})`;
ctx.fill();
}
}
function drawShootingStars(dt) {
for (const sh of shootingStars) {
if (sh.done) continue;
if (!sh.active) {
sh.delay -= dt;
if (sh.delay > 0) continue;
sh.active = true;
}
sh.life += dt;
sh.x += sh.vx * dt;
sh.y += sh.vy * dt;
// push a dense trail point every frame — no gaps between renders
sh.trail.push({x: sh.x, y: sh.y, t: sh.life});
// trim trail to the visual length window
const speedMag = Math.hypot(sh.vx, sh.vy);
const trailDuration = sh.len / speedMag;
while (sh.trail.length && (sh.life - sh.trail[0].t) > trailDuration) sh.trail.shift();
// fade in over first 8% of life, fade out over last 35%
const lifeRatio = sh.life / sh.maxLife;
let fade = 1;
if (lifeRatio < 0.08) fade = lifeRatio/0.08;
else if (lifeRatio > 0.65) fade = Math.max(0, 1 - (lifeRatio-0.65)/0.35);
if (sh.trail.length > 1 && fade > 0.01) {
for (let i=1;i<sh.trail.length;i++) {
const p0 = sh.trail[i-1], p1 = sh.trail[i];
const segAge = (sh.life - p1.t) / trailDuration; // 0 (new/head) -> 1 (old/tail)
const segOp = Math.max(0, (1 - segAge)) * fade * 0.85;
if (segOp <= 0.01) continue;
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.strokeStyle = `rgba(255,255,255,${segOp.toFixed(3)})`;
ctx.lineWidth = sh.thickness * (1 - segAge*0.6);
ctx.lineCap = "round";
ctx.stroke();
}
// bright head
const head = sh.trail[sh.trail.length-1];
ctx.beginPath();
ctx.arc(head.x, head.y, sh.thickness*0.9, 0, Math.PI*2);
ctx.fillStyle = `rgba(255,255,255,${(fade*0.95).toFixed(3)})`;
ctx.fill();
}
// respawn once fully done (off-screen or life exceeded) with fresh randomized params
if (sh.life > sh.maxLife || sh.x > W*1.15 || sh.y > H*1.15) {
const fresh = spawnShootingStar(2 + Math.random()*6);
Object.assign(sh, fresh);
}
}
}
function frame(now) {
const dt = Math.min((now - lastTime) / 1000, 0.05); // clamp for tab-switch jumps
lastTime = now;
elapsed += dt;
ctx.clearRect(0, 0, W, H);
drawStars(dt);
if (!isMobile) drawShootingStars(dt);
if (running) requestAnimationFrame(frame);
}
let running = true;
// adaptive perf ladder: drop shooting stars, then thin background stars, if fps sags
if (!prefersReducedMotion) {
let cuts = 0;
const steps = [
()=>{ shootingStars.length = 0; },
()=>{ for (let i=stars.length-1; i>=0; i-=2) stars.splice(i,1); }
];
let frames=0, winStart=0, low=0;
const TH=30, SAMPLE=1000, PAT=2;
const origFrame = frame;
let frameCount=0;
const perfTick = () => {
frameCount++;
const now = performance.now();
const el = now - winStart;
if (el >= SAMPLE) {
const fps = (frameCount*1000)/el;
frameCount = 0; winStart = now;
if (fps < TH) { if (++low >= PAT && cuts < steps.length) { low=0; steps[cuts](); cuts++; } } else low = 0;
}
if (cuts < steps.length && running) requestAnimationFrame(perfTick);
};
setTimeout(()=>{ winStart = performance.now(); requestAnimationFrame(perfTick); }, 1500);
}
requestAnimationFrame(frame);
})();
</script>
<script>
// Smooth open/close for the "Entry schema" <details> block.
// Native <details> can't transition display:none<->block, so we intercept
// the toggle, keep the element open during the close animation, and drive
// max-height/opacity in JS based on the content's real scrollHeight.
(()=>{
const details = document.getElementById("howto-details");
if (!details) return;
const summary = details.querySelector("summary");
const content = details.querySelector(".howto-content");
if (!summary || !content) return;
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let animating = false;
function openIt() {
details.open = true;
const target = content.scrollHeight;
content.style.maxHeight = target + "px";
animating = true;
content.addEventListener("transitionend", function onEnd(e){
if (e.propertyName !== "max-height") return;
content.removeEventListener("transitionend", onEnd);
content.style.maxHeight = "none"; // allow content reflow (e.g. resize) while open
animating = false;
});
}
function closeIt() {
// pin current height first so the transition has a start point
content.style.maxHeight = content.scrollHeight + "px";
requestAnimationFrame(()=>{
requestAnimationFrame(()=>{ content.style.maxHeight = "0px"; });
});
animating = true;
content.addEventListener("transitionend", function onEnd(e){
if (e.propertyName !== "max-height") return;
content.removeEventListener("transitionend", onEnd);
details.open = false;
animating = false;
});
}
summary.addEventListener("click", (e) => {
if (reduce) return; // let native behavior handle it
e.preventDefault();
if (animating) return;
if (details.open) closeIt(); else openIt();
});
})();
</script>
<script>
// Custom sort dropdown — drives the hidden real <select> so any existing
// script.js listener on #sort-select 'change' keeps working untouched.
(()=>{
const pill = document.getElementById("sort-pill");
const trigger = document.getElementById("sort-trigger");
const label = document.getElementById("sort-trigger-label");
const menu = document.getElementById("sort-menu");
const realSelect = document.getElementById("sort-select");
if (!pill || !trigger || !menu || !realSelect) return;
const items = Array.from(menu.querySelectorAll("li"));
function closeMenu() {
menu.classList.remove("show");
pill.classList.remove("open");
trigger.setAttribute("aria-expanded", "false");
setTimeout(()=>{ if (!menu.classList.contains("show")) menu.hidden = true; }, 150);
}
function openMenu() {
menu.hidden = false;
requestAnimationFrame(()=> menu.classList.add("show"));
pill.classList.add("open");
trigger.setAttribute("aria-expanded", "true");
const current = items.find(i => i.classList.contains("selected"));
(current || items[0]).focus();
}
function selectItem(item) {
items.forEach(i => { i.classList.remove("selected"); i.setAttribute("aria-selected","false"); });
item.classList.add("selected");
item.setAttribute("aria-selected","true");
label.textContent = item.textContent;
if (realSelect.value !== item.dataset.value) {
realSelect.value = item.dataset.value;
realSelect.dispatchEvent(new Event("change", { bubbles: true }));
}
closeMenu();
trigger.focus();
}
trigger.setAttribute("tabindex", "0");
items.forEach(i => i.setAttribute("tabindex", "-1"));
trigger.addEventListener("click", () => {
if (menu.classList.contains("show")) closeMenu(); else openMenu();
});
items.forEach(item => {
item.addEventListener("click", () => selectItem(item));
item.addEventListener("keydown", (e) => {
const idx = items.indexOf(item);
if (e.key === "ArrowDown") { e.preventDefault(); (items[idx+1] || items[0]).focus(); }
else if (e.key === "ArrowUp") { e.preventDefault(); (items[idx-1] || items[items.length-1]).focus(); }
else if (e.key === "Enter" || e.key === " ") { e.preventDefault(); selectItem(item); }
else if (e.key === "Escape") { e.preventDefault(); closeMenu(); trigger.focus(); }
else if (e.key === "Tab") { closeMenu(); }
});
});
trigger.addEventListener("keydown", (e) => {
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
e.preventDefault(); openMenu();
}
});
document.addEventListener("click", (e) => {
if (!pill.contains(e.target)) closeMenu();
});
})();
</script>
<script>
// Rotating headline – same rolling pattern as Members, but with leaderboard copy
(()=>{
const el=document.getElementById("rotating-headline");
if(!el) return;
const phrases=[
"Every evaluated model, every benchmark, one table.",
"Do you want to compare models?",
"Do you want to beat the best avg?",
"Looking for the smallest mighty model?",
"Dual-mode 7-2026: generative + loglikelihood.",
"Search by org, sort by params.",
"Simple, reliable, open sourced.",
"Got a model to benchmark?"
];
const reduce=window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let i=0;
setInterval(()=>{
i=(i+1)%phrases.length;
if(reduce){ el.textContent=phrases[i]; return; }
el.classList.add("rolling-out");
el.addEventListener("transitionend", function onOut(){
el.textContent=phrases[i];
el.classList.remove("rolling-out");
el.classList.add("rolling-in");
requestAnimationFrame(()=>requestAnimationFrame(()=>el.classList.remove("rolling-in")));
}, {once:true});
}, 3400);
})();
</script>
<script src="script.js" defer></script>
</body>
</html>