Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Voice of a Continent | SimbaBench Leaderboard</title> | |
| <link rel="icon" type="image/jpeg" href="https://africa.dlnlp.ai/simba/images/favicon.png"> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Rubik:wght@500;600;700;800&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --bg-deep: #1e1b4b; | |
| --text-yellow: #fbbf24; | |
| --text-white: #ffffff; | |
| --simba-navy: #0f172a; | |
| --border-gold: #dca02a; | |
| --grad-blue: linear-gradient(135deg, #3b82f6, #0ea5e9); | |
| --grad-red: linear-gradient(135deg, #ef4444, #f43f5e); | |
| --grad-purple: linear-gradient(135deg, #6366f1, #8b5cf6); | |
| --grad-orange: linear-gradient(135deg, #f97316, #fb923c); | |
| --grad-green: linear-gradient(135deg, #22c55e, #10b981); | |
| } | |
| body { | |
| background-color: var(--bg-deep); | |
| font-family: 'Inter', sans-serif; | |
| color: var(--text-white); | |
| margin: 0; padding: 0; | |
| overflow-x: hidden; | |
| } | |
| a { text-decoration: none; transition: 0.2s; } | |
| /* --- NAVBAR --- */ | |
| .navbar { padding: 20px 40px; display: flex; justify-content: space-between; align-items: center; position: relative; z-index: 100; } | |
| .nav-logo { color: white; font-family: 'Rubik', sans-serif; font-weight: 700; font-size: 22px; display: flex; align-items: center; gap: 12px; } | |
| .nav-logo img { height: 40px; } | |
| .nav-text { display: flex; flex-direction: column; line-height: 1.1; } | |
| .nav-text span:first-child { font-size: 14px; color: var(--text-yellow); } | |
| .nav-links { display: flex; gap: 25px; align-items: center; } | |
| .btn-submit { background: var(--grad-blue); color: white ; padding: 10px 24px; border-radius: 30px; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4); } | |
| /* --- HERO --- */ | |
| .hero { max-width: 1000px; margin: 60px auto 100px; padding: 0 40px; position: relative; text-align: center; } | |
| .hero-content { z-index: 2; display: flex; flex-direction: column; align-items: center; } | |
| .conf-badge { background: white; color: black; display: inline-flex; align-items: center; gap: 10px; padding: 6px 16px; border-radius: 8px; margin-bottom: 30px; font-weight: 700; font-size: 14px; } | |
| .conf-badge i { color: #cc0000; font-size: 18px; } | |
| .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; } | |
| .hero h2 { font-family: 'Rubik', sans-serif; font-size: 32px; color: var(--text-yellow); margin: 15px 0 30px; font-weight: 700; } | |
| .hero p { color: #cbd5e1; font-size: 18px; line-height: 1.6; max-width: 700px; margin-bottom: 50px; } | |
| .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; } | |
| .action-btn { display: flex; align-items: center; gap: 8px; padding: 12px 24px; border-radius: 30px; color: white; font-weight: 600; font-size: 15px; transition: transform 0.2s; } | |
| .action-btn:hover { transform: translateY(-3px); color: white; } | |
| .bg-red { background: var(--grad-red); } .bg-purple { background: var(--grad-purple); } | |
| /* --- CONTENT --- */ | |
| .content-container { width: 80%; max-width: 1600px; margin: 0 auto; } | |
| .main-card { background: white; border-radius: 24px; padding: 40px; color: #0f172a; min-height: 600px; position: relative; z-index: 10; margin-bottom: 60px; } | |
| /* Tabs */ | |
| .tabs { display: flex; gap: 10px; border-bottom: 2px solid #e2e8f0; margin-bottom: 30px; padding-bottom: 10px; overflow-x: auto; } | |
| .tab-btn { background: none; border: none; font-size: 15px; font-weight: 600; color: #64748b; padding: 10px 20px; cursor: pointer; transition: 0.3s; border-radius: 8px; white-space: nowrap; } | |
| .tab-btn:hover { background: #f1f5f9; color: var(--bg-deep); } | |
| .tab-btn.active { background: var(--bg-deep); color: white; } | |
| .view { display: none ; animation: fadeIn 0.4s; } | |
| .view.active { display: block ; } | |
| /* Controls */ | |
| .controls { background: #f8fafc; padding: 20px; border-radius: 12px; display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap; } | |
| select { padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1; font-size: 14px; min-width: 250px; cursor:pointer; } | |
| .rank-info { font-size: 13px; color: #64748b; margin-top: 5px; font-style: italic; } | |
| .score-desc { font-size: 14px; color: #475569; background: #fff7ed; border-left: 4px solid #f97316; padding: 10px 15px; margin-bottom: 20px; border-radius: 4px; } | |
| /* ASR Buttons */ | |
| .mode-btn { padding: 8px 20px; border-radius: 8px; font-weight: 600; font-size: 13px; cursor: pointer; transition: all 0.2s; border: 1px solid #e2e8f0; background: white; color: var(--simba-navy); } | |
| .mode-btn:hover { border-color: var(--text-yellow); color: var(--text-yellow); } | |
| .mode-btn.active { background-color: var(--simba-navy); color: white; border-color: var(--simba-navy); box-shadow: 0 4px 12px rgba(15, 23, 42, 0.15); } | |
| /* --- FANCY SUMMARY CARDS --- */ | |
| .summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 25px; margin-bottom: 50px; } | |
| .fancy-card { | |
| background: white; border-radius: 16px; padding: 25px; | |
| display: flex; align-items: center; gap: 20px; | |
| box-shadow: 0 10px 25px -5px rgba(0,0,0,0.08); border: 1px solid #f1f5f9; | |
| transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .fancy-card:hover { transform: translateY(-5px); box-shadow: 0 20px 30px -10px rgba(0,0,0,0.12); border-color: #e2e8f0; } | |
| .card-icon { | |
| width: 60px; height: 60px; border-radius: 14px; | |
| display: flex; align-items: center; justify-content: center; | |
| font-size: 26px; color: white; flex-shrink: 0; | |
| box-shadow: 0 8px 15px -3px rgba(0,0,0,0.2); | |
| } | |
| .icon-asr { background: var(--grad-blue); } | |
| .icon-tts { background: var(--grad-orange); } | |
| .icon-slid { background: var(--grad-green); } | |
| .card-content { flex: 1; } | |
| .card-title { font-size: 11px; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 5px; } | |
| .card-model { font-size: 18px; font-weight: 800; color: #0f172a; margin-bottom: 4px; line-height: 1.3; } | |
| .card-score { font-size: 14px; font-weight: 600; color: #475569; } | |
| .card-score b { color: #0f172a; } | |
| /* Table */ | |
| .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; margin-bottom: 30px; } | |
| table { width: 100%; border-collapse: collapse; font-size: 14px; } | |
| th { background: #f1f5f9; color: #0f172a; font-weight: 700; padding: 16px; text-align: center; border-bottom: 2px solid #e2e8f0; white-space: nowrap; vertical-align: bottom; } | |
| td { padding: 14px; border-bottom: 1px solid #e2e8f0; text-align: center; color: #334155; } | |
| tr:hover td { background: #f8fafc; } | |
| th:first-child, td:first-child { position: sticky; left: 0; background: white; z-index: 2; text-align: left; font-weight: 700; color: #0f172a; border-right: 2px solid #f1f5f9; min-width: 220px; } | |
| thead th:first-child { background: #f1f5f9; z-index: 3; } | |
| th:nth-child(2), td:nth-child(2) { text-align: left; } | |
| h4 { color: var(--text-yellow); font-family: 'Rubik', sans-serif; font-size: 18px; margin: 0 0 15px 0; border-bottom: 1px solid #eee; padding-bottom: 10px; } | |
| /* Citations */ | |
| .citation-section { background: #0f172a; border: 1px solid #334155; border-radius: 16px; padding: 40px; box-sizing: border-box; box-shadow: 0 20px 40px rgba(0,0,0,0.3); position: relative; margin-bottom: 60px; } | |
| .citation-section h3 { color: var(--text-yellow); font-family: 'Rubik', sans-serif; font-size: 24px; margin-top: 0; } | |
| .citation-box { background: #1e293b; color: #cbd5e1; padding: 20px; border-radius: 12px; font-family: monospace; white-space: pre-wrap; margin-top: 20px; overflow-x: auto; border: 1px solid #334155; font-size: 13px; line-height: 1.5; } | |
| .copy-btn { position: absolute; top: 40px; right: 40px; background: var(--text-yellow); border: none; padding: 8px 16px; border-radius: 6px; font-weight: bold; cursor: pointer; color: #0f172a; } | |
| .footer { text-align: center; padding: 40px; color: #94a3b8; font-size: 14px; } | |
| @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} } | |
| .arrow-down { color: #d97706; font-size: 10px; } | |
| .arrow-up { color: #16a34a; font-size: 10px; } | |
| .empty-msg { color: #64748b; font-style: italic; padding: 20px; text-align: center; } | |
| </style> | |
| </head> | |
| <body> | |
| <nav class="navbar layout-width"> | |
| <a href="https://africa.dlnlp.ai/simba" class="nav-logo"> | |
| <img src="https://africa.dlnlp.ai/simba/images/simba_main_logo.png" alt="Logo"> | |
| <div class="nav-text"><span>Voice of a</span> Continent</div> | |
| </a> | |
| <div class="nav-links"> | |
| <a href="https://aclanthology.org/2025.emnlp-main.559" target="_blank" class="nav-link">Paper</a> | |
| <a href="https://africa.dlnlp.ai/simba/" target="_blank" class="btn-submit">Submit New Results</a> | |
| </div> | |
| </nav> | |
| <div class="hero layout-width"> | |
| <div class="hero-content"> | |
| <div class="conf-badge"><i class="fa-solid fa-location-dot"></i> EMNLP 2025 Β· Suzhou, China</div> | |
| <h1>SimbaBench Leaderboard</h1> | |
| <h2>Mapping Africaβs Speech Technology</h2> | |
| <p>SimbaBench bridges the digital divide with a unified suite for African AI: the largest open-source speech benchmark covering 61 languages.</p> | |
| <div class="hero-actions"> | |
| <a href="https://aclanthology.org/2025.emnlp-main.559" target="_blank" class="action-btn bg-red"><i class="fa-regular fa-file-pdf"></i> Read Paper</a> | |
| <a href="https://africa.dlnlp.ai/simba" target="_blank" class="action-btn bg-purple"><i class="fa-solid fa-code-branch"></i> Official Website</a> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="content-container layout-width"> | |
| <div class="main-card" id="board"> | |
| <div id="loader" style="text-align:center; padding:60px; color:#64748b;"> | |
| <i class="fa-solid fa-circle-notch fa-spin fa-3x"></i><br><br>Loading Leaderboard Data... | |
| </div> | |
| <div id="app-content" style="display:none;"> | |
| <div class="tabs"> | |
| <button class="tab-btn active" id="tab-btn-asr" onclick="setTab('asr')">Automatic Speech Recognition (ASR)</button> | |
| <button class="tab-btn" id="tab-btn-tts" onclick="setTab('tts')">Text-to-Speech (TTS)</button> | |
| <button class="tab-btn" id="tab-btn-slid" onclick="setTab('slid')">Spoken Language Identification (SLID)</button> | |
| <button class="tab-btn" id="tab-btn-lang" onclick="setTab('lang')">Language Analysis</button> | |
| </div> | |
| <div id="asr" class="view active"> | |
| <div class="controls"> | |
| <span style="font-weight:700;">View Mode:</span> | |
| <button id="btn-over" class="mode-btn active" onclick="setASRMode('overview')">Overview</button> | |
| <button id="btn-fam" class="mode-btn" onclick="setASRMode('family')">By Family</button> | |
| <button id="btn-mod" class="mode-btn" onclick="setASRMode('model')">By Model</button> | |
| <div style="flex-grow:1; text-align:right;"> | |
| <div id="grp-fam" style="display:none;"><label>Select Family: </label><select id="asr-select-fam" onchange="renderASR()"></select></div> | |
| <div id="grp-mod" style="display:none;"><label>Select Model: </label><select id="asr-select-mod" onchange="renderASR()"></select></div> | |
| </div> | |
| </div> | |
| <div style="display:flex; justify-content:space-between; align-items:end; margin-bottom:10px;"> | |
| <h3 id="asr-title" style="color:#d97706; margin:0;">Results Overview</h3> | |
| <div id="asr-rank-info" class="rank-info">Ranking: WER (β) then CER (β)</div> | |
| </div> | |
| <div id="asr-desc"></div> | |
| <div id="asr-table" class="table-wrap"></div> | |
| </div> | |
| <div id="tts" class="view"> | |
| <div class="controls"> | |
| <span style="font-weight:600;">Select Model:</span> | |
| <select id="tts-select" onchange="renderTTS()"></select> | |
| </div> | |
| <h3 id="tts-title" style="color:#d97706; margin-bottom:10px;"></h3> | |
| <div id="tts-summary" style="margin-bottom:20px; font-weight:600; color:#0f172a;"></div> | |
| <div id="tts-table" class="table-wrap"></div> | |
| </div> | |
| <div id="slid" class="view"> | |
| <div style="display:flex; justify-content:space-between; align-items:end; margin-bottom:15px;"> | |
| <h3 style="color:#d97706; margin:0;">Spoken Language Identification</h3> | |
| <div class="rank-info">Ranking: F1-Macro (β)</div> | |
| </div> | |
| <div id="slid-table" class="table-wrap"></div> | |
| </div> | |
| <div id="lang" class="view"> | |
| <div class="controls"> | |
| <span style="font-weight:600;">Select Language:</span> | |
| <select id="lang-select" onchange="renderLanguageView()"></select> | |
| </div> | |
| <div id="lang-summary" class="summary-grid"></div> | |
| <h4>Automatic Speech Recognition (ASR) <span class="rank-info" style="font-weight:normal;">(Ranked by WER β, then CER β)</span></h4> | |
| <div id="lang-asr-table" class="table-wrap"></div> | |
| <h4>Text-to-Speech (TTS) Results</h4> | |
| <div id="lang-tts-table" class="table-wrap"></div> | |
| <h4>Spoken Language Identification (SLID) Results <span class="rank-info" style="font-weight:normal;">(Ranked by Score β)</span></h4> | |
| <div id="lang-slid-table" class="table-wrap"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="citation-section layout-width" id="citation"> | |
| <h3>Citation</h3> | |
| <button class="copy-btn" onclick="copyCitation()"><i class="fa-regular fa-copy"></i> Copy</button> | |
| <p style="color:#94a3b8; margin-bottom:20px;">If you use the Simba benchmark, please cite our paper:</p> | |
| <div class="citation-box" id="bibtex">@inproceedings{elmadany-etal-2025-voice, | |
| title = "Voice of a Continent: Mapping {A}frica{'}s Speech Technology Frontier", | |
| author = "Elmadany, AbdelRahim A. and Kwon, Sang Yun and Toyin, Hawau Olamide and Alcoba Inciarte, Alcides and Aldarmaki, Hanan and Abdul-Mageed, Muhammad", | |
| editor = "Christodoulopoulos, Christos and Chakraborty, Tanmoy and Rose, Carolyn and Peng, Violet", | |
| booktitle = "Proceedings of the 2025 Conference on Empirical Methods in Natural Language Processing", | |
| month = nov, | |
| year = "2025", | |
| address = "Suzhou, China", | |
| publisher = "Association for Computational Linguistics", | |
| url = "https://aclanthology.org/2025.emnlp-main.559/", | |
| doi = "10.18653/v1/2025.emnlp-main.559", | |
| pages = "11039--11061", | |
| ISBN = "979-8-89176-332-6", | |
| }</div> | |
| </div> | |
| </div> | |
| <div class="footer"> | |
| <img src="https://africa.dlnlp.ai/sahara//img/sahara_web_sponsers.jpg" width="250" style="opacity:0.8; margin-bottom:15px;"> | |
| <br>© 2025 Simba Benchmark. | |
| </div> | |
| <script> | |
| const NAME_TO_ISO = {'Afrikaans':'afr', 'Akuapim-twi':'ak', 'Amharic':'amh', 'Asante-twi':'tw', 'Bemba':'bem', 'Hausa':'hau', 'Igbo':'ibo', 'Yoruba':'yor', 'Zulu':'zul', 'Swahili':'swa', 'Somali':'som', 'Wolof':'wol', 'Xhosa':'xho'}; | |
| let DATA = null; | |
| let asrMode = 'overview'; | |
| fetch('/api/data').then(r=>r.json()).then(d=>{ | |
| DATA = d; | |
| document.getElementById('loader').style.display='none'; | |
| document.getElementById('app-content').style.display='block'; | |
| init(); | |
| }); | |
| function init() { | |
| populate('asr-select-fam', DATA.metadata.families); | |
| populate('asr-select-mod', DATA.metadata.models); | |
| populate('tts-select', DATA.metadata.tts_models); | |
| const allLangs = new Set(); | |
| // Collect languages from all sources to be thorough | |
| Object.values(DATA.asr.by_model).forEach(rows => rows.forEach(r => { if(r.Language) allLangs.add(r.Language); })); | |
| Object.values(DATA.tts).forEach(rows => rows.forEach(r => { const l = r.Language || r.language; if(l) allLangs.add(l); })); | |
| if(DATA.slid) DATA.slid.forEach(r => { if(r.Language) allLangs.add(r.Language); }); | |
| populate('lang-select', Array.from(allLangs).sort()); | |
| renderASR(); renderTTS(); renderSLID(); | |
| } | |
| function populate(id, list) { | |
| const s = document.getElementById(id); s.innerHTML=''; | |
| list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); }); | |
| } | |
| function fmt(n) { return (n==null)?'N/A':n.toFixed(2); } | |
| function getIso(name, dataIso) { | |
| let iso = dataIso || NAME_TO_ISO[name] || name.substring(0,3).toLowerCase(); | |
| return `${name} (${iso})`; | |
| } | |
| // --- ASR --- | |
| function renderASR() { | |
| const div = document.getElementById('asr-table'); | |
| const title = document.getElementById('asr-title'); | |
| const desc = document.getElementById('asr-desc'); | |
| let html=''; | |
| if (asrMode === 'overview') { | |
| title.innerText = "Results Overview (All Models)"; | |
| desc.innerHTML = `<p class="score-desc">The <b>Simba ASR Score</b> is the global average of WER and CER across all languages in the benchmark.</p>`; | |
| document.getElementById('grp-fam').style.display='none'; | |
| document.getElementById('grp-mod').style.display='none'; | |
| const modelStats = []; | |
| Object.keys(DATA.asr.by_model).forEach(modName => { | |
| const rows = DATA.asr.by_model[modName]; | |
| let sW=0, sC=0, n=0; | |
| rows.forEach(r => { sW+=r.WER; sC+=r.CER; n++; }); | |
| if(n>0) modelStats.push({ name: modName, wer: sW/n, cer: sC/n }); | |
| }); | |
| modelStats.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer)); | |
| html = `<table><thead><tr><th>Model</th><th>Simba ASR Score (WER/CER) <i class="fa-solid fa-arrow-down arrow-down"></i></th></tr></thead><tbody>`; | |
| let currentRank = 1; | |
| modelStats.forEach((m, i) => { | |
| if (i > 0 && Math.abs(m.wer - modelStats[i-1].wer) > 0.001) currentRank = i + 1; else currentRank = 1; | |
| let icon = (currentRank===1)?'π₯ ':(currentRank===2)?'π₯ ':(currentRank===3)?'π₯ ':''; | |
| html += `<tr><td style="font-weight:600; color:var(--simba-navy)">${icon}${m.name}</td><td><b>${fmt(m.wer)} / ${fmt(m.cer)}</b></td></tr>`; | |
| }); | |
| } else if(asrMode==='family'){ | |
| const val = document.getElementById('asr-select-fam').value; | |
| title.innerText = `Results for Family: ${val}`; | |
| desc.innerHTML = `<p class="score-desc">The <b>Family Score</b> is the average WER and CER calculated across all languages within the <b>${val}</b> family.</p>`; | |
| document.getElementById('grp-fam').style.display='inline-block'; | |
| document.getElementById('grp-mod').style.display='none'; | |
| const d = DATA.asr.by_family[val]; | |
| if(!d) return; | |
| d.data.sort((a,b) => a.Avg_WER - b.Avg_WER); | |
| html = `<table><thead><tr><th>Model</th><th>Simba ASR Family Score <i class="fa-solid fa-arrow-down arrow-down"></i></th>`; | |
| d.languages.forEach(l => html += `<th>${getIso(l.name, l.iso)}</th>`); | |
| html += `</tr></thead><tbody>`; | |
| let currentRank = 1; | |
| d.data.forEach((r, idx) => { | |
| if(idx > 0 && Math.abs(r.Avg_WER - d.data[idx-1].Avg_WER) > 0.001) currentRank = idx + 1; else currentRank = 1; | |
| let icon = (currentRank===1)?'π₯ ':(currentRank===2)?'π₯ ':(currentRank===3)?'π₯ ':''; | |
| html += `<tr><td style="font-weight:600; color:var(--simba-navy)">${icon}${r.Model}</td><td style="font-weight:bold; background:#f8fafc">${fmt(r.Avg_WER)} / ${fmt(r.Avg_CER)}</td>`; | |
| d.languages.forEach(l => { | |
| let w=r[`WER_${l.name}`], c=r[`CER_${l.name}`]; | |
| html += `<td>${(w!=null)?fmt(w)+'/'+fmt(c):'-'}</td>`; | |
| }); | |
| html += `</tr>`; | |
| }); | |
| } else { | |
| const val = document.getElementById('asr-select-mod').value; | |
| title.innerText = `Results for Model: ${val}`; | |
| desc.innerHTML = ""; | |
| document.getElementById('grp-fam').style.display='none'; | |
| document.getElementById('grp-mod').style.display='inline-block'; | |
| const rows = DATA.asr.by_model[val]; | |
| html = `<table><thead><tr><th>Language</th><th>Family</th><th>WER <i class="fa-solid fa-arrow-down arrow-down"></i></th><th>CER <i class="fa-solid fa-arrow-down arrow-down"></i></th></tr></thead><tbody>`; | |
| rows.forEach(r => { | |
| html += `<tr><td>${getIso(r.Language, r.ISO)}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`; | |
| }); | |
| } | |
| div.innerHTML = html; | |
| } | |
| // --- TTS --- | |
| function renderTTS() { | |
| const val = document.getElementById('tts-select').value; | |
| document.getElementById('tts-title').innerText = `Model: ${val}`; | |
| const rows = DATA.tts[val]; | |
| if(!rows) return; | |
| const metrics = [ | |
| {k:'wer', l:'WER(β)', h:false}, {k:'mcd', l:'MCD(β)', h:false}, | |
| {k:'logf0rmse', l:'LogF0RMSE(β)', h:false}, {k:'speech_token_distance', l:'SpeechTokenDistance(β)', h:false}, | |
| {k:'pesq', l:'PESQ(β)', h:true}, {k:'utmos', l:'UTMOS(β)', h:true}, | |
| {k:'speechbleu', l:'SpeechBLEU(β)', h:true}, {k:'speechbert_score', l:'SpeechBERTScore(β)', h:true} | |
| ]; | |
| let sums={}, c=0; | |
| metrics.forEach(m=>sums[m.k]=0); | |
| rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; }); | |
| document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + metrics.map(m=> `${m.l.replace('(β)','').replace('(β)','')}: ${fmt(sums[m.k]/c)}`).join(" | "); | |
| let html = `<table><thead><tr><th>Language</th>`; | |
| metrics.forEach(m => html += `<th>${m.l}</th>`); | |
| html += `</tr></thead><tbody>`; | |
| rows.forEach(r => { | |
| html += `<tr><td>${getIso(r.language, r.iso)}</td>`; | |
| metrics.forEach(m => html += `<td>${fmt(r[m.k])}</td>`); | |
| html += `</tr>`; | |
| }); | |
| document.getElementById('tts-table').innerHTML = html; | |
| } | |
| // --- SLID --- | |
| function renderSLID() { | |
| let html = `<table><thead><tr><th>Language</th><th>MMS-LID-1024 (β)</th><th>Simba-SLID (β)</th></tr></thead><tbody>`; | |
| DATA.slid.forEach(r => { | |
| let m = r['MMS-LID-1024'], s = r['Simba-SLID']; | |
| let mS = fmt(m), sS = fmt(s); | |
| if(m > s) mS = `<b>${mS} π₯</b>`; else if(s > m) sS = `<b>${sS} π₯</b>`; | |
| html += `<tr><td>${getIso(r.Language)}</td><td>${mS}</td><td>${sS}</td></tr>`; | |
| }); | |
| document.getElementById('slid-table').innerHTML = html; | |
| } | |
| // --- LANGUAGE VIEW --- | |
| function renderLanguageView() { | |
| const lang = document.getElementById('lang-select').value; | |
| // 1. ASR Data | |
| let asrRows = []; | |
| Object.keys(DATA.asr.by_model).forEach(modName => { | |
| const row = DATA.asr.by_model[modName].find(r => r.Language === lang); | |
| if(row) asrRows.push({model: modName, wer: row.WER, cer: row.CER}); | |
| }); | |
| asrRows.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer)); | |
| // 2. TTS Data | |
| let ttsRows = []; | |
| Object.keys(DATA.tts).forEach(mod => { | |
| const row = DATA.tts[mod].find(r => (r.Language || r.language) === lang); | |
| if(row) ttsRows.push({model: mod, ...row}); | |
| }); | |
| // 3. SLID Data | |
| let slidRows = []; | |
| const slidRow = DATA.slid.find(r => r.Language === lang) || | |
| DATA.slid.find(r => r.Language.startsWith(lang + " (")) || | |
| DATA.slid.find(r => r.Language.includes(lang)); | |
| if(slidRow) { | |
| const models = Object.keys(slidRow).filter(k => k !== 'Language' && k !== 'ISO'); | |
| models.forEach(m => slidRows.push({model: m, val: slidRow[m]})); | |
| slidRows.sort((a,b) => b.val - a.val); // Descending | |
| } | |
| // --- SUMMARY CARDS --- | |
| let summaryHTML = ""; | |
| // ASR Winner | |
| if(asrRows.length > 0) { | |
| summaryHTML += `<div class="fancy-card"> | |
| <div class="card-icon icon-asr"><i class="fa-solid fa-microphone-lines"></i></div> | |
| <div class="card-content"> | |
| <div class="card-title">Best ASR Model</div> | |
| <div class="card-model">${asrRows[0].model}</div> | |
| <div class="card-score">WER: <b>${fmt(asrRows[0].wer)}%</b></div> | |
| </div> | |
| </div>`; | |
| } else summaryHTML += `<div class="fancy-card"><div class="card-icon icon-asr"><i class="fa-solid fa-microphone-slash"></i></div><div class="card-content"><div class="card-title">ASR</div><div class="card-model">No Data</div></div></div>`; | |
| // TTS Winner | |
| if(ttsRows.length > 0) { | |
| const bestTTS = [...ttsRows].sort((a,b) => a.wer - b.wer)[0]; | |
| summaryHTML += `<div class="fancy-card"> | |
| <div class="card-icon icon-tts"><i class="fa-solid fa-volume-high"></i></div> | |
| <div class="card-content"> | |
| <div class="card-title">Best TTS (Intelligibility)</div> | |
| <div class="card-model">${bestTTS.model}</div> | |
| <div class="card-score">WER: <b>${fmt(bestTTS.wer)}%</b></div> | |
| </div> | |
| </div>`; | |
| } else summaryHTML += `<div class="fancy-card"><div class="card-icon icon-tts"><i class="fa-solid fa-volume-xmark"></i></div><div class="card-content"><div class="card-title">TTS</div><div class="card-model">No Data</div></div></div>`; | |
| // SLID Winner | |
| if(slidRows.length > 0) { | |
| summaryHTML += `<div class="fancy-card"> | |
| <div class="card-icon icon-slid"><i class="fa-solid fa-language"></i></div> | |
| <div class="card-content"> | |
| <div class="card-title">Best SLID Model</div> | |
| <div class="card-model">${slidRows[0].model}</div> | |
| <div class="card-score">F1 Score: <b>${fmt(slidRows[0].val)}%</b></div> | |
| </div> | |
| </div>`; | |
| } else summaryHTML += `<div class="fancy-card"><div class="card-icon icon-slid"><i class="fa-solid fa-circle-question"></i></div><div class="card-content"><div class="card-title">SLID</div><div class="card-model">No Data</div></div></div>`; | |
| document.getElementById('lang-summary').innerHTML = summaryHTML; | |
| // --- TABLES --- | |
| // ASR Table | |
| let h1 = `<table><thead><tr><th>Model</th><th>WER <i class="fa-solid fa-arrow-down arrow-down"></i></th><th>CER <i class="fa-solid fa-arrow-down arrow-down"></i></th></tr></thead><tbody>`; | |
| if(asrRows.length === 0) h1 += `<tr><td colspan="3" class="empty-msg">No ASR results available for ${lang}.</td></tr>`; | |
| else { | |
| let currentRank = 1; | |
| asrRows.forEach((r, i) => { | |
| if (i > 0 && Math.abs(r.wer - asrRows[i-1].wer) > 0.001) currentRank = i + 1; else currentRank = 1; | |
| let icon = (currentRank===1)?'π₯ ':(currentRank===2)?'π₯ ':(currentRank===3)?'π₯ ':''; | |
| h1 += `<tr><td style="font-weight:600; color:var(--simba-navy)">${icon}${r.model}</td><td>${fmt(r.wer)}</td><td>${fmt(r.cer)}</td></tr>`; | |
| }); | |
| } | |
| h1 += `</tbody></table>`; | |
| document.getElementById('lang-asr-table').innerHTML = h1; | |
| // TTS Table | |
| let h2 = `<table><thead><tr><th>Model</th> | |
| <th>WER(β)</th><th>MCD(β)</th><th>LogF0RMSE(β)</th><th>SpeechTokenDistance(β)</th> | |
| <th>PESQ(β)</th><th>UTMOS(β)</th><th>SpeechBLEU(β)</th><th>SpeechBERTScore(β)</th> | |
| </tr></thead><tbody>`; | |
| if(ttsRows.length === 0) h2 += `<tr><td colspan="9" class="empty-msg">No TTS results available for ${lang}.</td></tr>`; | |
| else { | |
| ttsRows.forEach(row => { | |
| h2 += `<tr> | |
| <td style="font-weight:600;">${row.model}</td> | |
| <td>${fmt(row.wer)}</td><td>${fmt(row.mcd)}</td> | |
| <td>${fmt(row.logf0rmse)}</td><td>${fmt(row.speech_token_distance)}</td> | |
| <td>${fmt(row.pesq)}</td><td>${fmt(row.utmos)}</td> | |
| <td>${fmt(row.speechbleu)}</td><td>${fmt(row.speechbert_score)}</td> | |
| </tr>`; | |
| }); | |
| } | |
| h2 += `</tbody></table>`; | |
| document.getElementById('lang-tts-table').innerHTML = h2; | |
| // SLID Table | |
| let h3 = `<table><thead><tr><th>Model</th><th>F1-Score <i class="fa-solid fa-arrow-up arrow-up"></i></th></tr></thead><tbody>`; | |
| if(slidRows.length === 0) h3 += `<tr><td colspan="2" class="empty-msg">No SLID results available for ${lang}.</td></tr>`; | |
| else { | |
| let currentRank = 1; | |
| slidRows.forEach((sc, i) => { | |
| if (i > 0 && Math.abs(sc.val - slidRows[i-1].val) > 0.001) currentRank = i + 1; else currentRank = 1; | |
| let icon = (currentRank===1)?'π₯ ':(currentRank===2)?'π₯ ':(currentRank===3)?'π₯ ':''; | |
| h3 += `<tr><td style="font-weight:600;">${icon}${sc.model}</td><td>${fmt(sc.val)}</td></tr>`; | |
| }); | |
| } | |
| h3 += `</tbody></table>`; | |
| document.getElementById('lang-slid-table').innerHTML = h3; | |
| } | |
| // --- UI --- | |
| window.setTab = (id, btn) => { | |
| document.querySelectorAll('.view').forEach(e => e.classList.remove('active')); | |
| document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active')); | |
| document.getElementById(id).classList.add('active'); | |
| if(btn) btn.classList.add('active'); else document.getElementById(`tab-btn-${id}`).classList.add('active'); | |
| if(id === 'lang') renderLanguageView(); | |
| }; | |
| window.setASRMode = (m) => { | |
| asrMode = m; | |
| ['btn-over','btn-fam','btn-mod'].forEach(b => document.getElementById(b).classList.remove('active')); | |
| document.getElementById(m==='overview'?'btn-over':(m==='family'?'btn-fam':'btn-mod')).classList.add('active'); | |
| renderASR(); | |
| } | |
| window.copyCitation = () => { | |
| const text = document.getElementById('bibtex').innerText; | |
| navigator.clipboard.writeText(text).then(() => { | |
| const btn = document.querySelector('.copy-btn'); | |
| btn.innerHTML = '<i class="fa-solid fa-check"></i> Copied!'; | |
| setTimeout(() => btn.innerHTML = '<i class="fa-regular fa-copy"></i> Copy', 2000); | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> |