elmadany commited on
Commit
69a51b4
Β·
verified Β·
1 Parent(s): 94c6dcf

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +84 -75
public/index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Voice of a Continent | SimbaBench</title>
7
  <link rel="icon" type="image/jpeg" href="https://africa.dlnlp.ai/simba/images/favicon.png">
8
 
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
@@ -41,48 +41,47 @@
41
  .nav-logo img { height: 40px; }
42
  .nav-text { display: flex; flex-direction: column; line-height: 1.1; }
43
  .nav-text span:first-child { font-size: 14px; color: var(--text-yellow); }
44
- .nav-links { display: flex; gap: 25px; align-items: center; }
45
- .nav-link { color: #e2e8f0; font-size: 15px; font-weight: 500; }
46
- .nav-link:hover { color: var(--text-yellow); }
47
  .btn-submit { background: var(--grad-blue); color: white !important; padding: 10px 24px; border-radius: 30px; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4); }
48
- .btn-login { background: var(--text-yellow); color: var(--bg-deep) !important; padding: 10px 24px; border-radius: 30px; font-weight: 700; font-size: 14px; box-shadow: 0 4px 12px rgba(251, 191, 36, 0.4); }
49
 
50
  /* --- HERO --- */
51
  .hero {
52
- /* Removed display: flex to stack content */
53
- max-width: 1400px;
54
  margin: 60px auto 100px;
55
  padding: 0 40px;
56
  position: relative;
57
- text-align: center; /* Center text */
58
  }
59
  .hero-content {
60
  z-index: 2;
61
  display: flex;
62
  flex-direction: column;
63
- align-items: center; /* Center children horizontally */
64
  }
65
  .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; }
66
  .conf-badge i { color: #cc0000; font-size: 18px; }
67
  .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; }
68
  .hero h2 { font-family: 'Rubik', sans-serif; font-size: 32px; color: var(--text-yellow); margin: 15px 0 30px; font-weight: 700; }
69
- .hero p { color: #cbd5e1; font-size: 18px; line-height: 1.6; max-width: 650px; margin-bottom: 50px; }
70
- .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; /* Center buttons */ }
71
  .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; }
72
  .action-btn:hover { transform: translateY(-3px); color: white; }
73
- .bg-red { background: var(--grad-red); } .bg-purple { background: var(--grad-purple); } .bg-orange { background: var(--grad-orange); } .bg-blue { background: var(--grad-blue); } .bg-green { background: var(--grad-green); }
 
 
 
 
 
 
 
74
 
75
- /* Removed .hero-visual and .hero-visual img styles */
76
-
77
- /* Updated .audio-wave for full width position */
78
  .audio-wave {
79
  height: 150px;
80
  display: flex;
81
  align-items: center;
82
  justify-content: center;
83
  gap: 6px;
84
- margin-top: 60px; /* Add space above */
85
- mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent); /* Adjusted mask for wider view */
86
  }
87
  .bar { width: 6px; background: #ffffff; border-radius: 99px; animation: bounce 1.2s ease-in-out infinite; }
88
  .bar:nth-child(odd) { animation-duration: 1.5s; } .bar:nth-child(2n) { animation-duration: 1.1s; }
@@ -104,6 +103,7 @@
104
  .controls { background: #f8fafc; padding: 20px; border-radius: 12px; display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap; }
105
  select { padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1; font-size: 14px; min-width: 250px; cursor:pointer; }
106
  .rank-info { font-size: 13px; color: #64748b; margin-top: 5px; font-style: italic; }
 
107
 
108
  /* Table */
109
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; margin-bottom: 30px; }
@@ -125,24 +125,20 @@
125
 
126
  .footer { text-align: center; padding: 40px; color: #94a3b8; font-size: 14px; }
127
  @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} }
 
 
 
128
  </style>
129
  </head>
130
  <body>
131
 
132
- <nav class="navbar">
133
- <a href="#" class="nav-logo">
134
- <img src="https://africa.dlnlp.ai/simba/images/favicon.png" alt="Logo">
135
  <div class="nav-text"><span>Voice of a</span> Continent</div>
136
  </a>
137
  <div class="nav-links">
138
- <a href="https://arxiv.org/abs/2510.17998" class="nav-link">Paper</a>
139
- <a href="https://github.com/UBC-NLP/simba" class="nav-link">Pipeline</a>
140
- <a href="https://huggingface.co/datasets/UBC-NLP/simba" class="nav-link">Dataset</a>
141
- <a href="#" class="nav-link">Models</a>
142
- <a href="#board" class="nav-link" style="color:var(--text-yellow)">Leaderboard</a>
143
- <a href="#citation" class="nav-link">Citation</a>
144
- <a href="#" class="btn-submit">Submit New Results</a>
145
- <a href="#" class="btn-login">Login</a>
146
  </div>
147
  </nav>
148
 
@@ -150,21 +146,20 @@
150
  <div class="wave-bg"></div>
151
  <div class="hero-content">
152
  <div class="conf-badge"><i class="fa-solid fa-location-dot"></i> EMNLP 2025 Β· Suzhou, China</div>
153
- <h1>Voice of a Continent</h1>
154
  <h2>Mapping Africa’s Speech Technology</h2>
155
  <p>SimbaBench bridges the digital divide with a unified suite for African AI: the largest open-source speech benchmark covering 61 languages.</p>
156
  <div class="hero-actions">
157
- <a href="#" class="action-btn bg-red"><i class="fa-regular fa-file-pdf"></i> Read Paper</a>
158
- <a href="#" class="action-btn bg-purple"><i class="fa-solid fa-code-branch"></i> Pipeline</a>
159
- <a href="#" class="action-btn bg-orange"><i class="fa-solid fa-database"></i> Dataset</a>
160
- <a href="#" class="action-btn bg-blue"><i class="fa-solid fa-bolt"></i> Models</a>
161
- <a href="#board" class="action-btn bg-green"><i class="fa-solid fa-trophy"></i> Leaderboard</a>
162
  </div>
 
 
163
  <div class="audio-wave">
164
  <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
165
  </div>
166
  </div>
167
- </div>
168
 
169
  <div class="content-container">
170
  <div class="main-card" id="board">
@@ -197,6 +192,8 @@
197
  <h3 id="asr-title" style="color:#d97706; margin:0;">Results Overview</h3>
198
  <div id="asr-rank-info" class="rank-info">Ranking: WER (↓) then CER (↓)</div>
199
  </div>
 
 
200
  <div id="asr-table" class="table-wrap"></div>
201
  </div>
202
 
@@ -302,10 +299,12 @@
302
  function renderASR() {
303
  const div = document.getElementById('asr-table');
304
  const title = document.getElementById('asr-title');
 
305
  let html='';
306
 
307
  if (asrMode === 'overview') {
308
  title.innerText = "Results Overview (All Models)";
 
309
  document.getElementById('grp-fam').style.display='none';
310
  document.getElementById('grp-mod').style.display='none';
311
 
@@ -320,21 +319,21 @@
320
  modelStats.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer)); // Sorting by WER then CER
321
 
322
  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>`;
 
 
 
323
  modelStats.forEach((m, i) => {
324
- let rank = 1;
325
- if (i > 0 && Math.abs(m.wer - modelStats[i-1].wer) > 0.001) rank = i + 1;
326
- else if (i > 0) rank = 1; // Actually logic needs to track currentRank variable, but simplifying for medals
327
-
328
- // Simple medal logic: if tied with prev, use prev medal.
329
- // Accurate Ranking:
330
- let icon = '';
331
- // Re-evaluate rank based on index isn't enough for ties.
332
- // Let's just use top 3 logic based on sorted order for simplicity or strict rank variable.
333
- // Strict Rank Variable:
334
- if(i===0) m.rank=1;
335
- else if(Math.abs(m.wer - modelStats[i-1].wer) < 0.001) m.rank = modelStats[i-1].rank;
336
- else m.rank = i+1;
337
 
 
338
  if(m.rank === 1) icon = 'πŸ₯‡ '; else if(m.rank === 2) icon = 'πŸ₯ˆ '; else if(m.rank === 3) icon = 'πŸ₯‰ ';
339
 
340
  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>`;
@@ -343,6 +342,7 @@
343
  } else if(asrMode==='family'){
344
  const val = document.getElementById('asr-select-fam').value;
345
  title.innerText = `Results for Family: ${val}`;
 
346
  document.getElementById('grp-fam').style.display='inline-block';
347
  document.getElementById('grp-mod').style.display='none';
348
 
@@ -355,11 +355,15 @@
355
  d.languages.forEach(l => html += `<th>${getIso(l.name, l.iso)}</th>`);
356
  html += `</tr></thead><tbody>`;
357
 
 
358
  d.data.forEach((r, idx) => {
359
- // Rank Logic
360
- if(idx===0) r.rank=1;
361
- else if(Math.abs(r.Avg_WER - d.data[idx-1].Avg_WER) < 0.001) r.rank = d.data[idx-1].rank;
362
- else r.rank = idx+1;
 
 
 
363
 
364
  let icon = '';
365
  if(r.rank === 1) icon = 'πŸ₯‡ '; else if(r.rank === 2) icon = 'πŸ₯ˆ '; else if(r.rank === 3) icon = 'πŸ₯‰ ';
@@ -374,11 +378,12 @@
374
  } else {
375
  const val = document.getElementById('asr-select-mod').value;
376
  title.innerText = `Results for Model: ${val}`;
 
377
  document.getElementById('grp-fam').style.display='none';
378
  document.getElementById('grp-mod').style.display='inline-block';
379
 
380
  const rows = DATA.asr.by_model[val];
381
- html = `<table><thead><tr><th>Language (iso)</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>`;
382
  rows.forEach(r => {
383
  html += `<tr><td>${getIso(r.Language, r.ISO)}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
384
  });
@@ -405,7 +410,7 @@
405
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
406
  document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + metrics.map(m=> `${m.l.replace('(↓)','').replace('(↑)','')}: ${fmt(sums[m.k]/c)}`).join(" | ");
407
 
408
- let html = `<table><thead><tr><th>Language (iso)</th>`;
409
  metrics.forEach(m => html += `<th>${m.l}</th>`);
410
  html += `</tr></thead><tbody>`;
411
  rows.forEach(r => {
@@ -418,7 +423,7 @@
418
 
419
  // --- RENDER SLID ---
420
  function renderSLID() {
421
- let html = `<table><thead><tr><th>Language (iso)</th><th>MMS-LID-1024 (↑)</th><th>Simba-SLID (↑)</th></tr></thead><tbody>`;
422
  DATA.slid.forEach(r => {
423
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
424
  let mS = fmt(m), sS = fmt(s);
@@ -432,24 +437,26 @@
432
  function renderLanguageView() {
433
  const lang = document.getElementById('lang-select').value;
434
 
435
- // 1. ASR (Tie-Aware, WER then CER)
436
  let asrRows = [];
437
  Object.keys(DATA.asr.by_model).forEach(modName => {
438
  const row = DATA.asr.by_model[modName].find(r => r.Language === lang);
439
  if(row) asrRows.push({model: modName, wer: row.WER, cer: row.CER});
440
  });
441
-
442
- // Sort: Primary WER ASC, Secondary CER ASC
443
  asrRows.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer));
444
 
445
- let h1 = `<table><thead><tr><th>Model</th><th>WER</th><th>CER</th></tr></thead><tbody>`;
446
  if(asrRows.length === 0) h1 += `<tr><td colspan="3">No ASR data for this language.</td></tr>`;
447
  else {
 
448
  asrRows.forEach((r, i) => {
449
- // Calculate Rank
450
- if(i===0) r.rank=1;
451
- else if(Math.abs(r.wer - asrRows[i-1].wer) < 0.001) r.rank = asrRows[i-1].rank; // Tie
452
- else r.rank = i+1;
 
 
 
453
 
454
  let icon = '';
455
  if(r.rank === 1) icon = 'πŸ₯‡ '; else if(r.rank === 2) icon = 'πŸ₯ˆ '; else if(r.rank === 3) icon = 'πŸ₯‰ ';
@@ -459,10 +466,10 @@
459
  h1 += `</tbody></table>`;
460
  document.getElementById('lang-asr-table').innerHTML = h1;
461
 
462
- // 2. TTS (All Metrics)
463
  let h2 = `<table><thead><tr><th>Model</th>
464
- <th>WER(↓)</th><th>MCD(↓)</th><th>LogF0(↓)</th><th>SpTok(↓)</th>
465
- <th>PESQ(↑)</th><th>UTMOS(↑)</th><th>SpBLEU(↑)</th><th>SpBERT(↑)</th>
466
  </tr></thead><tbody>`;
467
 
468
  let ttsFound = false;
@@ -483,23 +490,25 @@
483
  h2 += `</tbody></table>`;
484
  document.getElementById('lang-tts-table').innerHTML = h2;
485
 
486
- // 3. SLID (Dynamic Models)
487
- let h3 = `<table><thead><tr><th>Model</th><th>F1-Score</th></tr></thead><tbody>`;
488
- // Fuzzy match logic
489
  const slidRow = DATA.slid.find(r => r.Language === lang) ||
490
  DATA.slid.find(r => r.Language.startsWith(lang + " (")) ||
491
  DATA.slid.find(r => r.Language.includes(lang));
492
 
493
  if(slidRow) {
494
- // Extract models dynamically (exclude Language key)
495
  const models = Object.keys(slidRow).filter(k => k !== 'Language' && k !== 'ISO');
496
- const scores = models.map(m => ({n: m, v: slidRow[m]})).sort((a,b) => b.v - a.v); // Descending
497
 
 
498
  scores.forEach((sc, i) => {
499
- // Rank logic for SLID
500
- if(i===0) sc.rank=1;
501
- else if(Math.abs(sc.v - scores[i-1].v) < 0.001) sc.rank = scores[i-1].rank;
502
- else sc.rank = i+1;
 
 
 
503
 
504
  let icon = '';
505
  if(sc.rank === 1) icon = 'πŸ₯‡ '; else if(sc.rank === 2) icon = 'πŸ₯ˆ '; else if(sc.rank === 3) icon = 'πŸ₯‰ ';
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Voice of a Continent | SimbaBench Leaderboard</title>
7
  <link rel="icon" type="image/jpeg" href="https://africa.dlnlp.ai/simba/images/favicon.png">
8
 
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
 
41
  .nav-logo img { height: 40px; }
42
  .nav-text { display: flex; flex-direction: column; line-height: 1.1; }
43
  .nav-text span:first-child { font-size: 14px; color: var(--text-yellow); }
 
 
 
44
  .btn-submit { background: var(--grad-blue); color: white !important; padding: 10px 24px; border-radius: 30px; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4); }
 
45
 
46
  /* --- HERO --- */
47
  .hero {
48
+ max-width: 1000px;
 
49
  margin: 60px auto 100px;
50
  padding: 0 40px;
51
  position: relative;
52
+ text-align: center;
53
  }
54
  .hero-content {
55
  z-index: 2;
56
  display: flex;
57
  flex-direction: column;
58
+ align-items: center;
59
  }
60
  .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; }
61
  .conf-badge i { color: #cc0000; font-size: 18px; }
62
  .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; }
63
  .hero h2 { font-family: 'Rubik', sans-serif; font-size: 32px; color: var(--text-yellow); margin: 15px 0 30px; font-weight: 700; }
64
+ .hero p { color: #cbd5e1; font-size: 18px; line-height: 1.6; max-width: 700px; margin-bottom: 50px; }
65
+ .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; }
66
  .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; }
67
  .action-btn:hover { transform: translateY(-3px); color: white; }
68
+ .bg-red { background: var(--grad-red); } .bg-purple { background: var(--grad-purple); }
69
+
70
+ /* Wave Animation */
71
+ .wave-bg {
72
+ position: absolute; top: -50px; left: -50%; width: 200%; height: 100%;
73
+ background: radial-gradient(circle, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0) 70%);
74
+ z-index: 0; pointer-events: none;
75
+ }
76
 
 
 
 
77
  .audio-wave {
78
  height: 150px;
79
  display: flex;
80
  align-items: center;
81
  justify-content: center;
82
  gap: 6px;
83
+ margin-top: 60px; /* Space above wave */
84
+ mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent);
85
  }
86
  .bar { width: 6px; background: #ffffff; border-radius: 99px; animation: bounce 1.2s ease-in-out infinite; }
87
  .bar:nth-child(odd) { animation-duration: 1.5s; } .bar:nth-child(2n) { animation-duration: 1.1s; }
 
103
  .controls { background: #f8fafc; padding: 20px; border-radius: 12px; display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap; }
104
  select { padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1; font-size: 14px; min-width: 250px; cursor:pointer; }
105
  .rank-info { font-size: 13px; color: #64748b; margin-top: 5px; font-style: italic; }
106
+ .score-desc { font-size: 14px; color: #475569; background: #fff7ed; border-left: 4px solid #f97316; padding: 10px 15px; margin-bottom: 20px; border-radius: 4px; }
107
 
108
  /* Table */
109
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; margin-bottom: 30px; }
 
125
 
126
  .footer { text-align: center; padding: 40px; color: #94a3b8; font-size: 14px; }
127
  @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} }
128
+
129
+ .arrow-down { color: #d97706; font-size: 10px; }
130
+ .arrow-up { color: #16a34a; font-size: 10px; }
131
  </style>
132
  </head>
133
  <body>
134
 
135
+ <nav class="navbar layout-width">
136
+ <a href="https://africa.dlnlp.ai/simba" class="nav-logo">
137
+ <img src="https://africa.dlnlp.ai/simba/images/simba_main_logo.png" alt="Logo">
138
  <div class="nav-text"><span>Voice of a</span> Continent</div>
139
  </a>
140
  <div class="nav-links">
141
+ <a href="https://africa.dlnlp.ai/simba/" target="_blank" class="btn-submit">Submit New Results</a>
 
 
 
 
 
 
 
142
  </div>
143
  </nav>
144
 
 
146
  <div class="wave-bg"></div>
147
  <div class="hero-content">
148
  <div class="conf-badge"><i class="fa-solid fa-location-dot"></i> EMNLP 2025 Β· Suzhou, China</div>
149
+ <h1>SimbaBench Leaderboard</h1>
150
  <h2>Mapping Africa’s Speech Technology</h2>
151
  <p>SimbaBench bridges the digital divide with a unified suite for African AI: the largest open-source speech benchmark covering 61 languages.</p>
152
  <div class="hero-actions">
153
+ <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>
154
+ <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>
 
 
 
155
  </div>
156
+ </div>
157
+ <div class="hero-visual">
158
  <div class="audio-wave">
159
  <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
160
  </div>
161
  </div>
162
+ </div>
163
 
164
  <div class="content-container">
165
  <div class="main-card" id="board">
 
192
  <h3 id="asr-title" style="color:#d97706; margin:0;">Results Overview</h3>
193
  <div id="asr-rank-info" class="rank-info">Ranking: WER (↓) then CER (↓)</div>
194
  </div>
195
+ <div id="asr-desc"></div>
196
+
197
  <div id="asr-table" class="table-wrap"></div>
198
  </div>
199
 
 
299
  function renderASR() {
300
  const div = document.getElementById('asr-table');
301
  const title = document.getElementById('asr-title');
302
+ const desc = document.getElementById('asr-desc');
303
  let html='';
304
 
305
  if (asrMode === 'overview') {
306
  title.innerText = "Results Overview (All Models)";
307
+ 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>`;
308
  document.getElementById('grp-fam').style.display='none';
309
  document.getElementById('grp-mod').style.display='none';
310
 
 
319
  modelStats.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer)); // Sorting by WER then CER
320
 
321
  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>`;
322
+
323
+ // Tie-Aware Ranking Logic
324
+ let currentRank = 1;
325
  modelStats.forEach((m, i) => {
326
+ if (i > 0) {
327
+ const prev = modelStats[i-1];
328
+ if (Math.abs(m.wer - prev.wer) > 0.001) {
329
+ currentRank = i + 1;
330
+ }
331
+ } else {
332
+ currentRank = 1;
333
+ }
334
+ m.rank = currentRank;
 
 
 
 
335
 
336
+ let icon = '';
337
  if(m.rank === 1) icon = 'πŸ₯‡ '; else if(m.rank === 2) icon = 'πŸ₯ˆ '; else if(m.rank === 3) icon = 'πŸ₯‰ ';
338
 
339
  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>`;
 
342
  } else if(asrMode==='family'){
343
  const val = document.getElementById('asr-select-fam').value;
344
  title.innerText = `Results for Family: ${val}`;
345
+ 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>`;
346
  document.getElementById('grp-fam').style.display='inline-block';
347
  document.getElementById('grp-mod').style.display='none';
348
 
 
355
  d.languages.forEach(l => html += `<th>${getIso(l.name, l.iso)}</th>`);
356
  html += `</tr></thead><tbody>`;
357
 
358
+ let currentRank = 1;
359
  d.data.forEach((r, idx) => {
360
+ if(idx > 0) {
361
+ const prev = d.data[idx-1];
362
+ if (Math.abs(r.Avg_WER - prev.Avg_WER) > 0.001) currentRank = idx + 1;
363
+ } else {
364
+ currentRank = 1;
365
+ }
366
+ r.rank = currentRank;
367
 
368
  let icon = '';
369
  if(r.rank === 1) icon = 'πŸ₯‡ '; else if(r.rank === 2) icon = 'πŸ₯ˆ '; else if(r.rank === 3) icon = 'πŸ₯‰ ';
 
378
  } else {
379
  const val = document.getElementById('asr-select-mod').value;
380
  title.innerText = `Results for Model: ${val}`;
381
+ desc.innerHTML = "";
382
  document.getElementById('grp-fam').style.display='none';
383
  document.getElementById('grp-mod').style.display='inline-block';
384
 
385
  const rows = DATA.asr.by_model[val];
386
+ 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>`;
387
  rows.forEach(r => {
388
  html += `<tr><td>${getIso(r.Language, r.ISO)}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
389
  });
 
410
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
411
  document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + metrics.map(m=> `${m.l.replace('(↓)','').replace('(↑)','')}: ${fmt(sums[m.k]/c)}`).join(" | ");
412
 
413
+ let html = `<table><thead><tr><th>Language</th>`;
414
  metrics.forEach(m => html += `<th>${m.l}</th>`);
415
  html += `</tr></thead><tbody>`;
416
  rows.forEach(r => {
 
423
 
424
  // --- RENDER SLID ---
425
  function renderSLID() {
426
+ let html = `<table><thead><tr><th>Language</th><th>MMS-LID-1024 (↑)</th><th>Simba-SLID (↑)</th></tr></thead><tbody>`;
427
  DATA.slid.forEach(r => {
428
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
429
  let mS = fmt(m), sS = fmt(s);
 
437
  function renderLanguageView() {
438
  const lang = document.getElementById('lang-select').value;
439
 
440
+ // 1. ASR
441
  let asrRows = [];
442
  Object.keys(DATA.asr.by_model).forEach(modName => {
443
  const row = DATA.asr.by_model[modName].find(r => r.Language === lang);
444
  if(row) asrRows.push({model: modName, wer: row.WER, cer: row.CER});
445
  });
 
 
446
  asrRows.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer));
447
 
448
+ 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>`;
449
  if(asrRows.length === 0) h1 += `<tr><td colspan="3">No ASR data for this language.</td></tr>`;
450
  else {
451
+ let currentRank = 1;
452
  asrRows.forEach((r, i) => {
453
+ if (i > 0) {
454
+ const prev = asrRows[i-1];
455
+ if (Math.abs(r.wer - prev.wer) > 0.001) currentRank = i + 1;
456
+ } else {
457
+ currentRank = 1;
458
+ }
459
+ r.rank = currentRank;
460
 
461
  let icon = '';
462
  if(r.rank === 1) icon = 'πŸ₯‡ '; else if(r.rank === 2) icon = 'πŸ₯ˆ '; else if(r.rank === 3) icon = 'πŸ₯‰ ';
 
466
  h1 += `</tbody></table>`;
467
  document.getElementById('lang-asr-table').innerHTML = h1;
468
 
469
+ // 2. TTS
470
  let h2 = `<table><thead><tr><th>Model</th>
471
+ <th>WER(↓)</th><th>MCD(↓)</th><th>LogF0RMSE(↓)</th><th>SpeechTokenDistance(↓)</th>
472
+ <th>PESQ(↑)</th><th>UTMOS(↑)</th><th>SpeechBLEU(↑)</th><th>SpeechBERTScore(↑)</th>
473
  </tr></thead><tbody>`;
474
 
475
  let ttsFound = false;
 
490
  h2 += `</tbody></table>`;
491
  document.getElementById('lang-tts-table').innerHTML = h2;
492
 
493
+ // 3. SLID
494
+ 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>`;
 
495
  const slidRow = DATA.slid.find(r => r.Language === lang) ||
496
  DATA.slid.find(r => r.Language.startsWith(lang + " (")) ||
497
  DATA.slid.find(r => r.Language.includes(lang));
498
 
499
  if(slidRow) {
 
500
  const models = Object.keys(slidRow).filter(k => k !== 'Language' && k !== 'ISO');
501
+ const scores = models.map(m => ({n: m, v: slidRow[m]})).sort((a,b) => b.v - a.v);
502
 
503
+ let currentRank = 1;
504
  scores.forEach((sc, i) => {
505
+ if(i > 0) {
506
+ const prev = scores[i-1];
507
+ if (Math.abs(sc.v - prev.v) > 0.001) currentRank = i + 1;
508
+ } else {
509
+ currentRank = 1;
510
+ }
511
+ sc.rank = currentRank;
512
 
513
  let icon = '';
514
  if(sc.rank === 1) icon = 'πŸ₯‡ '; else if(sc.rank === 2) icon = 'πŸ₯ˆ '; else if(sc.rank === 3) icon = 'πŸ₯‰ ';