elmadany commited on
Commit
a1ec447
Β·
verified Β·
1 Parent(s): 1f1ccf0

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +153 -251
public/index.html CHANGED
@@ -16,17 +16,14 @@
16
  --bg-deep: #1e1b4b; /* Deep Navy */
17
  --text-yellow: #fbbf24; /* Brand Yellow */
18
  --text-white: #ffffff;
19
- --text-mute: #94a3b8;
20
-
21
  /* Gradients */
22
  --grad-blue: linear-gradient(90deg, #3b82f6, #0ea5e9);
23
  --grad-red: linear-gradient(135deg, #ef4444, #f43f5e);
24
  --grad-purple: linear-gradient(135deg, #6366f1, #8b5cf6);
25
  --grad-orange: linear-gradient(135deg, #f97316, #fb923c);
26
  --grad-green: linear-gradient(135deg, #22c55e, #10b981);
27
-
28
- --simba-navy: #0f172a;
29
- --border-gold: #dca02a;
30
  }
31
 
32
  body {
@@ -42,18 +39,11 @@
42
  /* --- NAVBAR --- */
43
  .navbar {
44
  padding: 20px 40px;
45
- display: flex;
46
- justify-content: space-between;
47
- align-items: center;
48
- background: transparent;
49
- z-index: 100;
50
- position: relative;
51
  }
52
  .nav-logo {
53
- color: white;
54
- font-family: 'Rubik', sans-serif;
55
- font-weight: 700;
56
- font-size: 22px;
57
  display: flex; align-items: center; gap: 12px;
58
  }
59
  .nav-logo img { height: 40px; }
@@ -65,48 +55,17 @@
65
  .nav-link:hover { color: var(--text-yellow); }
66
 
67
  .btn-submit {
68
- background: var(--grad-blue);
69
- color: white !important;
70
- padding: 10px 24px;
71
- border-radius: 30px;
72
- font-weight: 600;
73
- font-size: 14px;
74
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
75
  }
76
  .btn-login {
77
- background: var(--text-yellow);
78
- color: var(--bg-deep) !important;
79
- padding: 10px 24px;
80
- border-radius: 30px;
81
- font-weight: 700;
82
- font-size: 14px;
83
- box-shadow: 0 4px 12px rgba(251, 191, 36, 0.4);
84
  }
85
 
86
  /* --- HERO --- */
87
- .hero {
88
- display: flex;
89
- align-items: center;
90
- max-width: 1400px;
91
- margin: 40px auto 80px;
92
- padding: 0 40px;
93
- position: relative;
94
- }
95
-
96
  .hero-content { flex: 1; z-index: 2; }
97
 
98
- .conf-badge {
99
- background: white;
100
- color: black;
101
- display: inline-flex;
102
- align-items: center;
103
- gap: 10px;
104
- padding: 6px 16px;
105
- border-radius: 8px;
106
- margin-bottom: 30px;
107
- font-weight: 700;
108
- font-size: 14px;
109
- }
110
  .conf-badge i { color: #cc0000; font-size: 18px; }
111
 
112
  .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; }
@@ -123,66 +82,27 @@
123
  .bg-blue { background: var(--grad-blue); box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3); }
124
  .bg-green { background: var(--grad-green); box-shadow: 0 8px 20px rgba(34, 197, 94, 0.3); }
125
 
126
- /* --- CSS AUDIO WAVE VISUALIZER --- */
127
- .hero-visual {
128
- flex: 1;
129
- display: flex;
130
- justify-content: flex-end;
131
- position: relative;
132
- height: 400px;
133
- align-items: center;
134
- }
135
  .hero-visual img { max-width: 100%; height: auto; z-index: 2; position: relative; }
136
 
 
137
  .audio-wave {
138
- position: absolute;
139
- bottom: 50px;
140
- left: 0;
141
- right: 0;
142
- height: 150px;
143
- display: flex;
144
- align-items: center;
145
- justify-content: center;
146
- gap: 6px;
147
- z-index: 0;
148
- opacity: 0.3;
149
  mask-image: linear-gradient(to right, transparent, black 20%, black 80%, transparent);
150
  }
151
- .bar {
152
- width: 6px;
153
- background: #ffffff;
154
- border-radius: 99px;
155
- animation: bounce 1.2s ease-in-out infinite;
156
- }
157
- /* Randomize animation delays for wave effect */
158
  .bar:nth-child(odd) { animation-duration: 1.5s; }
159
  .bar:nth-child(2n) { animation-duration: 1.1s; }
160
- .bar:nth-child(3n) { animation-duration: 1.3s; }
161
- .bar:nth-child(4n) { animation-duration: 0.9s; }
162
 
163
- @keyframes bounce {
164
- 0%, 100% { height: 20px; }
165
- 50% { height: 120px; }
166
- }
167
-
168
- /* --- MAIN CONTENT AREA --- */
169
  .main-card {
170
- background: white;
171
- border-radius: 24px;
172
- margin: 0 40px;
173
- padding: 40px;
174
- color: #0f172a;
175
- min-height: 600px;
176
- position: relative;
177
- z-index: 10;
178
  }
179
 
180
- /* Tabs */
181
  .tabs { display: flex; gap: 15px; border-bottom: 2px solid #e2e8f0; margin-bottom: 30px; padding-bottom: 10px; }
182
- .tab-btn {
183
- background: none; border: none; font-size: 16px; font-weight: 600; color: #64748b;
184
- padding: 10px 20px; cursor: pointer; transition: 0.3s; border-radius: 8px;
185
- }
186
  .tab-btn:hover { background: #f1f5f9; color: var(--bg-deep); }
187
  .tab-btn.active { background: var(--bg-deep); color: white; }
188
 
@@ -191,8 +111,10 @@
191
 
192
  /* Controls */
193
  .controls { background: #f8fafc; padding: 20px; border-radius: 12px; display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap; }
194
- select { padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1; font-size: 14px; min-width: 250px; cursor:pointer;}
195
 
 
 
196
  /* Table */
197
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; }
198
  table { width: 100%; border-collapse: collapse; font-size: 14px; }
@@ -200,48 +122,37 @@
200
  td { padding: 14px; border-bottom: 1px solid #e2e8f0; text-align: center; color: #334155; }
201
  tr:hover td { background: #f8fafc; }
202
 
203
- 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; }
204
  thead th:first-child { background: #f1f5f9; z-index: 3; }
205
  th:nth-child(2), td:nth-child(2) { text-align: left; }
206
 
207
- /* --- SEPARATE CITATION SECTION --- */
208
  .citation-section {
209
- max-width: 1000px;
210
- margin: 60px auto;
211
  background: #0f172a;
212
  border: 1px solid #334155;
213
  border-radius: 16px;
214
  padding: 40px;
 
215
  box-shadow: 0 20px 40px rgba(0,0,0,0.3);
 
216
  }
217
- .citation-section h3 {
218
- color: var(--text-yellow);
219
- font-family: 'Rubik', sans-serif;
220
- font-size: 24px;
221
- margin-top: 0;
222
- }
223
  .citation-box {
224
- background: #1e293b;
225
- color: #cbd5e1;
226
- padding: 20px;
227
- border-radius: 12px;
228
- font-family: monospace;
229
- white-space: pre-wrap;
230
- margin-top: 20px;
231
- overflow-x: auto;
232
- border: 1px solid #334155;
233
  }
 
 
 
 
 
 
234
 
235
  .footer { text-align: center; padding-bottom: 40px; color: #94a3b8; font-size: 14px; }
236
-
237
  @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} }
238
-
239
- /* Mobile */
240
- @media (max-width: 900px) {
241
- .hero { flex-direction: column; text-align: center; padding: 20px; }
242
- .hero-visual { display: none; } /* Hide map on small screens */
243
- .nav-links { display: none; }
244
- }
245
  </style>
246
  </head>
247
  <body>
@@ -249,17 +160,14 @@
249
  <nav class="navbar">
250
  <a href="#" class="nav-logo">
251
  <img src="https://africa.dlnlp.ai/simba/images/favicon.png" alt="Logo">
252
- <div class="nav-text">
253
- <span>Voice of a</span>
254
- Continent
255
- </div>
256
  </a>
257
  <div class="nav-links">
258
  <a href="https://arxiv.org/abs/2510.17998" class="nav-link">Paper</a>
259
  <a href="https://github.com/UBC-NLP/simba" class="nav-link">Pipeline</a>
260
  <a href="https://huggingface.co/datasets/UBC-NLP/simba" class="nav-link">Dataset</a>
261
  <a href="#" class="nav-link">Models</a>
262
- <a href="#" class="nav-link" style="color:var(--text-yellow)">Leaderboard</a>
263
  <a href="#citation" class="nav-link">Citation</a>
264
  <a href="#" class="btn-submit">Submit New Results</a>
265
  <a href="#" class="btn-login">Login</a>
@@ -267,56 +175,34 @@
267
  </nav>
268
 
269
  <div class="hero">
 
270
  <div class="hero-content">
271
- <div class="conf-badge">
272
- <i class="fa-solid fa-location-dot"></i> EMNLP 2025 Β· Suzhou, China
273
- </div>
274
-
275
  <h1>Voice of a Continent</h1>
276
  <h2>Mapping Africa’s Speech Technology</h2>
277
-
278
- <p>
279
- SimbaBench bridges the digital divide with a unified suite for African AI: the largest
280
- open-source speech benchmark covering 61 languages, best-in-class multilingual models,
281
- and an interactive leaderboard driving the future of speech technology.
282
- </p>
283
-
284
  <div class="hero-actions">
285
- <a href="https://arxiv.org/abs/2510.17998" target="_blank" class="action-btn bg-red">
286
- <i class="fa-regular fa-file-pdf"></i> Read Paper
287
- </a>
288
- <a href="https://github.com/UBC-NLP/simba" target="_blank" class="action-btn bg-purple">
289
- <i class="fa-solid fa-code-branch"></i> Pipeline
290
- </a>
291
- <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank" class="action-btn bg-orange">
292
- <i class="fa-solid fa-database"></i> Dataset
293
- </a>
294
- <a href="#" class="action-btn bg-blue">
295
- <i class="fa-solid fa-bolt"></i> Models
296
- </a>
297
- <a href="#board" class="action-btn bg-green">
298
- <i class="fa-solid fa-trophy"></i> Leaderboard
299
- </a>
300
  </div>
301
  </div>
302
-
303
  <div class="hero-visual">
304
  <div class="audio-wave">
305
- <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
306
- <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
307
- <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
308
- <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
309
- <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
310
- <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
311
- <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
312
  </div>
313
  <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Map">
314
  </div>
315
  </div>
316
 
317
  <div class="main-card" id="board">
318
-
319
- <div id="loader"><i class="fa-solid fa-circle-notch fa-spin fa-2x" style="color:#0f172a"></i><br>Loading Data...</div>
 
320
 
321
  <div id="app-content" style="display:none;">
322
  <div class="tabs">
@@ -333,17 +219,17 @@
333
 
334
  <div style="flex-grow:1; text-align:right;">
335
  <span style="font-weight:600; margin-right:10px;">Select:</span>
336
- <div id="grp-fam" style="display:inline-block;">
337
- <select id="asr-select-fam" onchange="renderASR()"></select>
338
- </div>
339
- <div id="grp-mod" style="display:none;">
340
- <select id="asr-select-mod" onchange="renderASR()"></select>
341
- </div>
342
  </div>
343
  </div>
344
 
345
- <h3 id="asr-title" style="color:#d97706; margin-bottom:15px;"></h3>
346
- <div id="asr-legend" style="margin-bottom:15px; font-size:13px; color:#64748b; line-height:1.6;"></div>
 
 
 
 
347
  <div id="asr-table" class="table-wrap"></div>
348
  </div>
349
 
@@ -358,61 +244,63 @@
358
  </div>
359
 
360
  <div id="slid" class="view">
361
- <h3 style="color:#d97706;">Spoken Language Identification Leaderboard</h3>
 
 
 
362
  <div id="slid-table" class="table-wrap"></div>
363
  </div>
364
  </div>
365
- </div>
366
 
367
- <div class="citation-section" id="citation">
368
- <h3>Citation</h3>
369
- <p style="color:#94a3b8; margin-bottom:20px;">If you use the Simba benchmark, please cite our <a href="https://africa.dlnlp.ai/sahara/" style="color:#fbbf24; font-weight:bold;">ACL2025 paper</a>.</p>
370
- <div class="citation-box">@inproceedings{simba2025,
371
- title={Simba: A Unified Benchmark for Speech Tasks in African Languages},
372
- author={Put Authors Here},
373
- booktitle={ACL},
374
- year={2025}
 
 
 
 
 
 
 
 
 
375
  }</div>
 
376
  </div>
377
 
378
  <div class="footer">
379
  <img src="https://africa.dlnlp.ai/sahara//img/sahara_web_sponsers.jpg" width="250" style="opacity:0.8; margin-bottom:15px;">
380
- <br>
381
- &copy; 2025 Simba Benchmark.
382
  </div>
383
 
384
  <script>
385
  // --- LOGIC ---
386
- // Basic mapping for common ISO codes if missing from data
387
  const NAME_TO_ISO = {
388
- 'Afrikaans': 'afr', 'Akuapim-twi': 'ak', 'Amharic': 'amh', 'Asante-twi': 'tw',
389
- 'Bemba': 'bem', 'Hausa': 'hau', 'Igbo': 'ibo', 'Yoruba': 'yor', 'Zulu': 'zul',
390
- 'Swahili': 'swa', 'Somali': 'som', 'Wolof': 'wol', 'Xhosa': 'xho'
391
  };
392
 
393
  let DATA = null;
394
  let asrMode = 'family';
395
 
396
- // Load Data
397
  fetch('/api/data').then(r=>r.json()).then(d=>{
398
  DATA = d;
399
  document.getElementById('loader').style.display='none';
400
  document.getElementById('app-content').style.display='block';
401
  init();
402
- }).catch(e=>{
403
- document.getElementById('loader').innerHTML = `<span style="color:red">Failed to load data.</span>`;
404
- });
405
 
406
  function init() {
407
  populate('asr-select-fam', DATA.metadata.families);
408
  populate('asr-select-mod', DATA.metadata.models);
409
  populate('tts-select', DATA.metadata.tts_models);
410
-
411
- // Set Initial State Explicitly
412
- setTab('asr');
413
- renderASR();
414
- renderTTS();
415
- renderSLID();
416
  }
417
 
418
  function populate(id, list) {
@@ -420,131 +308,145 @@
420
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
421
  }
422
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
423
- // Fallback to name-based ISO if data property missing
424
- function getIso(name, dataIso) { return dataIso || NAME_TO_ISO[name] || name.substring(0,3).toLowerCase(); }
 
 
425
 
426
- // --- ASR ---
427
  function renderASR() {
428
  const div = document.getElementById('asr-table');
429
  const legend = document.getElementById('asr-legend');
 
430
  let html='';
431
 
432
  if(asrMode==='family'){
433
  const val = document.getElementById('asr-select-fam').value;
434
  document.getElementById('asr-title').innerText = `Results for Family: ${val}`;
 
435
 
436
  const d = DATA.asr.by_family[val];
437
  if(!d) return;
438
 
439
- // Legend
440
- legend.style.display = 'block';
441
- legend.innerHTML = `<b>Codes:</b> ` + d.languages.map(l => {
442
- // l is object {name: 'Amharic', iso: 'amh'} from server.js
443
- return `<span style="margin-right:12px"><b>${l.iso}</b>=${l.name}</span>`;
444
- }).join("");
445
-
446
- // Table
447
- html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down arrow-down"></i></th>`;
448
- d.languages.forEach(l => html += `<th>${l.iso}</th>`);
449
  html += `</tr></thead><tbody>`;
450
 
451
- d.data.forEach(r => {
452
- html += `<tr><td>${r.Model}</td><td style="font-weight:bold; background:#f8fafc">${fmt(r.Avg_WER)} / ${fmt(r.Avg_CER)}</td>`;
 
 
 
 
 
 
453
  d.languages.forEach(l => {
454
  let w=r[`WER_${l.name}`], c=r[`CER_${l.name}`];
455
  html += `<td>${(w!=null)?fmt(w)+'/'+fmt(c):'-'}</td>`;
456
  });
457
  html += `</tr>`;
458
  });
 
 
459
  } else {
460
  const val = document.getElementById('asr-select-mod').value;
461
  document.getElementById('asr-title').innerText = `Results for Model: ${val}`;
462
- legend.style.display = 'none';
463
 
464
  const rows = DATA.asr.by_model[val];
465
- 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>`;
466
  rows.forEach(r => {
467
- html += `<tr><td>${r.Language}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
468
  });
469
  }
470
  div.innerHTML = html;
471
  }
472
 
473
- // --- TTS ---
474
  function renderTTS() {
475
  const val = document.getElementById('tts-select').value;
476
- document.getElementById('tts-title').innerText = `Results for Model: ${val}`;
477
-
478
  const rows = DATA.tts[val];
479
  if(!rows) return;
480
 
481
  const metrics = [
482
- {k:'wer', l:'WER', h:false}, {k:'mcd', l:'MCD', h:false},
483
- {k:'logf0rmse', l:'LogF0', h:false}, {k:'speech_token_distance', l:'SpTokDist', h:false},
484
- {k:'pesq', l:'PESQ', h:true}, {k:'utmos', l:'UTMOS', h:true},
485
- {k:'speechbleu', l:'SpBLEU', h:true}, {k:'speechbert_score', l:'SpBERT', h:true}
486
  ];
487
 
488
  let sums={}, c=0;
489
  metrics.forEach(m=>sums[m.k]=0);
490
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
491
 
492
- let sumHTML = metrics.map(m => `<span>${m.l}: <b>${fmt(sums[m.k]/c)}</b></span>`).join(" | ");
493
- document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + sumHTML;
494
-
495
- let html = `<table><thead><tr><th>Language</th>`;
496
- metrics.forEach(m => {
497
- let icon = m.h ? 'up' : 'down';
498
- let col = m.h ? 'green' : '#d97706';
499
- html += `<th>${m.l} <i class="fa-solid fa-arrow-${icon}" style="color:${col}"></i></th>`;
500
- });
501
  html += `</tr></thead><tbody>`;
502
  rows.forEach(r => {
503
- html += `<tr><td>${r.language}</td>`;
504
  metrics.forEach(m => html += `<td>${fmt(r[m.k])}</td>`);
505
  html += `</tr>`;
506
  });
507
  document.getElementById('tts-table').innerHTML = html;
508
  }
509
 
510
- // --- SLID ---
511
  function renderSLID() {
512
- let html = `<table><thead><tr><th>Language</th><th>MMS <i class="fa-solid fa-arrow-up arrow-up"></i></th><th>Simba <i class="fa-solid fa-arrow-up arrow-up"></i></th></tr></thead><tbody>`;
 
 
 
 
513
  DATA.slid.forEach(r => {
514
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
515
  let mS = fmt(m), sS = fmt(s);
516
- if(m > s) mS = `<b style="color:green">${mS}</b>`; else if(s > m) sS = `<b style="color:green">${sS}</b>`;
517
- html += `<tr><td>${r.Language}</td><td>${mS}</td><td>${sS}</td></tr>`;
 
 
 
518
  });
519
  document.getElementById('slid-table').innerHTML = html;
520
  }
521
 
522
- // --- UI INTERACTION ---
523
  window.setTab = (id) => {
524
- // Hide all views
525
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
526
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
527
-
528
- // Show selected
529
- document.getElementById(id).classList.add('active');
530
  document.getElementById(`tab-btn-${id}`).classList.add('active');
531
  };
532
 
533
  window.setASRMode = (m) => {
534
  asrMode = m;
 
535
  document.getElementById('grp-fam').style.display = m==='family'?'inline-block':'none';
536
  document.getElementById('grp-mod').style.display = m==='model'?'inline-block':'none';
537
- const b1 = document.getElementById('btn-fam');
538
- const b2 = document.getElementById('btn-mod');
539
  if(m==='family') {
540
- b1.className = "action-btn bg-blue"; b1.style.color="white"; b1.style.background="";
541
- b2.className = "action-btn"; b2.style.background="#e2e8f0"; b2.style.color="#334155";
542
  } else {
543
- b2.className = "action-btn bg-blue"; b2.style.color="white"; b2.style.background="";
544
- b1.className = "action-btn"; b1.style.background="#e2e8f0"; b1.style.color="#334155";
545
  }
546
  renderASR();
547
  }
 
 
 
 
 
 
 
 
 
548
  </script>
549
  </body>
550
  </html>
 
16
  --bg-deep: #1e1b4b; /* Deep Navy */
17
  --text-yellow: #fbbf24; /* Brand Yellow */
18
  --text-white: #ffffff;
19
+ --simba-navy: #0f172a;
20
+ --border-gold: #dca02a;
21
  /* Gradients */
22
  --grad-blue: linear-gradient(90deg, #3b82f6, #0ea5e9);
23
  --grad-red: linear-gradient(135deg, #ef4444, #f43f5e);
24
  --grad-purple: linear-gradient(135deg, #6366f1, #8b5cf6);
25
  --grad-orange: linear-gradient(135deg, #f97316, #fb923c);
26
  --grad-green: linear-gradient(135deg, #22c55e, #10b981);
 
 
 
27
  }
28
 
29
  body {
 
39
  /* --- NAVBAR --- */
40
  .navbar {
41
  padding: 20px 40px;
42
+ display: flex; justify-content: space-between; align-items: center;
43
+ position: relative; z-index: 100;
 
 
 
 
44
  }
45
  .nav-logo {
46
+ color: white; font-family: 'Rubik', sans-serif; font-weight: 700; font-size: 22px;
 
 
 
47
  display: flex; align-items: center; gap: 12px;
48
  }
49
  .nav-logo img { height: 40px; }
 
55
  .nav-link:hover { color: var(--text-yellow); }
56
 
57
  .btn-submit {
58
+ 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);
 
 
 
 
 
 
59
  }
60
  .btn-login {
61
+ 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);
 
 
 
 
 
 
62
  }
63
 
64
  /* --- HERO --- */
65
+ .hero { display: flex; align-items: center; max-width: 1400px; margin: 60px auto 100px; padding: 0 40px; position: relative; }
 
 
 
 
 
 
 
 
66
  .hero-content { flex: 1; z-index: 2; }
67
 
68
+ .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; }
 
 
 
 
 
 
 
 
 
 
 
69
  .conf-badge i { color: #cc0000; font-size: 18px; }
70
 
71
  .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; }
 
82
  .bg-blue { background: var(--grad-blue); box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3); }
83
  .bg-green { background: var(--grad-green); box-shadow: 0 8px 20px rgba(34, 197, 94, 0.3); }
84
 
85
+ .hero-visual { flex: 1; display: flex; justify-content: flex-end; position: relative; height: 400px; align-items: center; }
 
 
 
 
 
 
 
 
86
  .hero-visual img { max-width: 100%; height: auto; z-index: 2; position: relative; }
87
 
88
+ /* Audio Wave Animation */
89
  .audio-wave {
90
+ position: absolute; bottom: 50px; left: 0; right: 0; height: 150px;
91
+ display: flex; align-items: center; justify-content: center; gap: 6px; z-index: 0; opacity: 0.3;
 
 
 
 
 
 
 
 
 
92
  mask-image: linear-gradient(to right, transparent, black 20%, black 80%, transparent);
93
  }
94
+ .bar { width: 6px; background: #ffffff; border-radius: 99px; animation: bounce 1.2s ease-in-out infinite; }
 
 
 
 
 
 
95
  .bar:nth-child(odd) { animation-duration: 1.5s; }
96
  .bar:nth-child(2n) { animation-duration: 1.1s; }
97
+ @keyframes bounce { 0%, 100% { height: 20px; } 50% { height: 120px; } }
 
98
 
99
+ /* --- MAIN BOARD --- */
 
 
 
 
 
100
  .main-card {
101
+ background: white; border-radius: 24px; margin: 0 40px; padding: 40px; color: #0f172a; min-height: 600px; position: relative; z-index: 10;
 
 
 
 
 
 
 
102
  }
103
 
 
104
  .tabs { display: flex; gap: 15px; border-bottom: 2px solid #e2e8f0; margin-bottom: 30px; padding-bottom: 10px; }
105
+ .tab-btn { background: none; border: none; font-size: 16px; font-weight: 600; color: #64748b; padding: 10px 20px; cursor: pointer; transition: 0.3s; border-radius: 8px; }
 
 
 
106
  .tab-btn:hover { background: #f1f5f9; color: var(--bg-deep); }
107
  .tab-btn.active { background: var(--bg-deep); color: white; }
108
 
 
111
 
112
  /* Controls */
113
  .controls { background: #f8fafc; padding: 20px; border-radius: 12px; display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap; }
114
+ select { padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1; font-size: 14px; min-width: 250px; cursor:pointer; }
115
 
116
+ .rank-info { font-size: 13px; color: #64748b; margin-top: 5px; font-style: italic; }
117
+
118
  /* Table */
119
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; }
120
  table { width: 100%; border-collapse: collapse; font-size: 14px; }
 
122
  td { padding: 14px; border-bottom: 1px solid #e2e8f0; text-align: center; color: #334155; }
123
  tr:hover td { background: #f8fafc; }
124
 
125
+ 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: 200px; }
126
  thead th:first-child { background: #f1f5f9; z-index: 3; }
127
  th:nth-child(2), td:nth-child(2) { text-align: left; }
128
 
129
+ /* Citations */
130
  .citation-section {
131
+ width: 100%; /* Full Width */
132
+ margin: 60px 0;
133
  background: #0f172a;
134
  border: 1px solid #334155;
135
  border-radius: 16px;
136
  padding: 40px;
137
+ box-sizing: border-box; /* Ensure padding doesn't overflow width */
138
  box-shadow: 0 20px 40px rgba(0,0,0,0.3);
139
+ position: relative;
140
  }
141
+ .citation-section h3 { color: var(--text-yellow); font-family: 'Rubik', sans-serif; font-size: 24px; margin-top: 0; }
 
 
 
 
 
142
  .citation-box {
143
+ background: #1e293b; color: #cbd5e1; padding: 20px; border-radius: 12px;
144
+ font-family: monospace; white-space: pre-wrap; margin-top: 20px;
145
+ overflow-x: auto; border: 1px solid #334155; font-size: 13px; line-height: 1.5;
 
 
 
 
 
 
146
  }
147
+ .copy-btn {
148
+ position: absolute; top: 40px; right: 40px;
149
+ background: var(--text-yellow); border: none; padding: 8px 16px;
150
+ border-radius: 6px; font-weight: bold; cursor: pointer; color: #0f172a;
151
+ }
152
+ .copy-btn:active { transform: scale(0.95); }
153
 
154
  .footer { text-align: center; padding-bottom: 40px; color: #94a3b8; font-size: 14px; }
 
155
  @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} }
 
 
 
 
 
 
 
156
  </style>
157
  </head>
158
  <body>
 
160
  <nav class="navbar">
161
  <a href="#" class="nav-logo">
162
  <img src="https://africa.dlnlp.ai/simba/images/favicon.png" alt="Logo">
163
+ <div class="nav-text"><span>Voice of a</span> Continent</div>
 
 
 
164
  </a>
165
  <div class="nav-links">
166
  <a href="https://arxiv.org/abs/2510.17998" class="nav-link">Paper</a>
167
  <a href="https://github.com/UBC-NLP/simba" class="nav-link">Pipeline</a>
168
  <a href="https://huggingface.co/datasets/UBC-NLP/simba" class="nav-link">Dataset</a>
169
  <a href="#" class="nav-link">Models</a>
170
+ <a href="#board" class="nav-link" style="color:var(--text-yellow)">Leaderboard</a>
171
  <a href="#citation" class="nav-link">Citation</a>
172
  <a href="#" class="btn-submit">Submit New Results</a>
173
  <a href="#" class="btn-login">Login</a>
 
175
  </nav>
176
 
177
  <div class="hero">
178
+ <div class="wave-bg"></div>
179
  <div class="hero-content">
180
+ <div class="conf-badge"><i class="fa-solid fa-location-dot"></i> EMNLP 2025 Β· Suzhou, China</div>
 
 
 
181
  <h1>Voice of a Continent</h1>
182
  <h2>Mapping Africa’s Speech Technology</h2>
183
+ <p>SimbaBench bridges the digital divide with a unified suite for African AI: the largest open-source speech benchmark covering 61 languages.</p>
 
 
 
 
 
 
184
  <div class="hero-actions">
185
+ <a href="https://arxiv.org/abs/2510.17998" target="_blank" class="action-btn bg-red"><i class="fa-regular fa-file-pdf"></i> Read Paper</a>
186
+ <a href="https://github.com/UBC-NLP/simba" target="_blank" class="action-btn bg-purple"><i class="fa-solid fa-code-branch"></i> Pipeline</a>
187
+ <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank" class="action-btn bg-orange"><i class="fa-solid fa-database"></i> Dataset</a>
188
+ <a href="#" class="action-btn bg-blue"><i class="fa-solid fa-bolt"></i> Models</a>
189
+ <a href="#board" class="action-btn bg-green"><i class="fa-solid fa-trophy"></i> Leaderboard</a>
 
 
 
 
 
 
 
 
 
 
190
  </div>
191
  </div>
 
192
  <div class="hero-visual">
193
  <div class="audio-wave">
194
+ <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>
195
+ <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>
196
+ <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>
 
 
 
 
197
  </div>
198
  <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Map">
199
  </div>
200
  </div>
201
 
202
  <div class="main-card" id="board">
203
+ <div id="loader" style="text-align:center; padding:60px; color:#64748b;">
204
+ <i class="fa-solid fa-circle-notch fa-spin fa-3x"></i><br><br>Loading Leaderboard Data...
205
+ </div>
206
 
207
  <div id="app-content" style="display:none;">
208
  <div class="tabs">
 
219
 
220
  <div style="flex-grow:1; text-align:right;">
221
  <span style="font-weight:600; margin-right:10px;">Select:</span>
222
+ <div id="grp-fam" style="display:inline-block;"><select id="asr-select-fam" onchange="renderASR()"></select></div>
223
+ <div id="grp-mod" style="display:none;"><select id="asr-select-mod" onchange="renderASR()"></select></div>
 
 
 
 
224
  </div>
225
  </div>
226
 
227
+ <div style="display:flex; justify-content:space-between; align-items:end; margin-bottom:10px;">
228
+ <h3 id="asr-title" style="color:#d97706; margin:0;"></h3>
229
+ <div id="asr-rank-info" class="rank-info" style="display:none;">Ranking: WER (↓) then CER (↓)</div>
230
+ </div>
231
+
232
+ <div id="asr-legend" style="margin-bottom:15px; font-size:13px; color:#64748b; background:#f8fafc; padding:10px; border-radius:6px; display:none;"></div>
233
  <div id="asr-table" class="table-wrap"></div>
234
  </div>
235
 
 
244
  </div>
245
 
246
  <div id="slid" class="view">
247
+ <div style="display:flex; justify-content:space-between; align-items:end; margin-bottom:15px;">
248
+ <h3 style="color:#d97706; margin:0;">Spoken Language Identification</h3>
249
+ <div class="rank-info">Ranking: F1-Macro (↑)</div>
250
+ </div>
251
  <div id="slid-table" class="table-wrap"></div>
252
  </div>
253
  </div>
 
254
 
255
+ <div class="citation-section" id="citation">
256
+ <h3>Citation</h3>
257
+ <button class="copy-btn" onclick="copyCitation()"><i class="fa-regular fa-copy"></i> Copy</button>
258
+ <p style="color:#94a3b8; margin-bottom:20px;">If you use the Simba benchmark, please cite our <a href="https://aclanthology.org/2025.emnlp-main.559/" style="color:#fbbf24; font-weight:bold;">EMNLP 2025 paper</a>.</p>
259
+ <div class="citation-box" id="bibtex">@inproceedings{elmadany-etal-2025-voice,
260
+ title = "Voice of a Continent: Mapping {A}frica{'}s Speech Technology Frontier",
261
+ author = "Elmadany, AbdelRahim A. and Kwon, Sang Yun and Toyin, Hawau Olamide and Alcoba Inciarte, Alcides and Aldarmaki, Hanan and Abdul-Mageed, Muhammad",
262
+ editor = "Christodoulopoulos, Christos and Chakraborty, Tanmoy and Rose, Carolyn and Peng, Violet",
263
+ booktitle = "Proceedings of the 2025 Conference on Empirical Methods in Natural Language Processing",
264
+ month = nov,
265
+ year = "2025",
266
+ address = "Suzhou, China",
267
+ publisher = "Association for Computational Linguistics",
268
+ url = "https://aclanthology.org/2025.emnlp-main.559/",
269
+ doi = "10.18653/v1/2025.emnlp-main.559",
270
+ pages = "11039--11061",
271
+ ISBN = "979-8-89176-332-6",
272
  }</div>
273
+ </div>
274
  </div>
275
 
276
  <div class="footer">
277
  <img src="https://africa.dlnlp.ai/sahara//img/sahara_web_sponsers.jpg" width="250" style="opacity:0.8; margin-bottom:15px;">
278
+ <br>&copy; 2025 Simba Benchmark.
 
279
  </div>
280
 
281
  <script>
282
  // --- LOGIC ---
 
283
  const NAME_TO_ISO = {
284
+ 'Afrikaans':'afr', 'Akuapim-twi':'ak', 'Amharic':'amh', 'Asante-twi':'tw', 'Bemba':'bem',
285
+ 'Hausa':'hau', 'Igbo':'ibo', 'Yoruba':'yor', 'Zulu':'zul', 'Swahili':'swa', 'Somali':'som',
286
+ 'Wolof':'wol', 'Xhosa':'xho', 'Lingala':'ln', 'Oromo':'om'
287
  };
288
 
289
  let DATA = null;
290
  let asrMode = 'family';
291
 
 
292
  fetch('/api/data').then(r=>r.json()).then(d=>{
293
  DATA = d;
294
  document.getElementById('loader').style.display='none';
295
  document.getElementById('app-content').style.display='block';
296
  init();
297
+ }).catch(e=>{ document.getElementById('loader').innerHTML=`<span style="color:red">Failed to load data.</span>`; });
 
 
298
 
299
  function init() {
300
  populate('asr-select-fam', DATA.metadata.families);
301
  populate('asr-select-mod', DATA.metadata.models);
302
  populate('tts-select', DATA.metadata.tts_models);
303
+ setTab('asr'); renderASR(); renderTTS(); renderSLID();
 
 
 
 
 
304
  }
305
 
306
  function populate(id, list) {
 
308
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
309
  }
310
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
311
+ function getIso(name, dataIso) {
312
+ let iso = dataIso || NAME_TO_ISO[name] || name.substring(0,3).toLowerCase();
313
+ return `${name} (${iso})`;
314
+ }
315
 
316
+ // --- RENDER ASR ---
317
  function renderASR() {
318
  const div = document.getElementById('asr-table');
319
  const legend = document.getElementById('asr-legend');
320
+ const rankInfo = document.getElementById('asr-rank-info');
321
  let html='';
322
 
323
  if(asrMode==='family'){
324
  const val = document.getElementById('asr-select-fam').value;
325
  document.getElementById('asr-title').innerText = `Results for Family: ${val}`;
326
+ rankInfo.style.display = 'block';
327
 
328
  const d = DATA.asr.by_family[val];
329
  if(!d) return;
330
 
331
+ // Sort By WER ascending
332
+ d.data.sort((a,b) => a.Avg_WER - b.Avg_WER);
333
+
334
+ html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down" style="color:#d97706; font-size:10px"></i></th>`;
335
+ d.languages.forEach(l => html += `<th>${getIso(l.name, l.iso)}</th>`);
 
 
 
 
 
336
  html += `</tr></thead><tbody>`;
337
 
338
+ d.data.forEach((r, idx) => {
339
+ // Ranking Icons
340
+ let rankIcon = '';
341
+ if(idx === 0) rankIcon = 'πŸ₯‡ ';
342
+ else if(idx === 1) rankIcon = 'πŸ₯ˆ ';
343
+ else if(idx === 2) rankIcon = 'πŸ₯‰ ';
344
+
345
+ html += `<tr><td>${rankIcon}${r.Model}</td><td style="font-weight:bold; background:#f8fafc">${fmt(r.Avg_WER)} / ${fmt(r.Avg_CER)}</td>`;
346
  d.languages.forEach(l => {
347
  let w=r[`WER_${l.name}`], c=r[`CER_${l.name}`];
348
  html += `<td>${(w!=null)?fmt(w)+'/'+fmt(c):'-'}</td>`;
349
  });
350
  html += `</tr>`;
351
  });
352
+ // Legend
353
+ legend.style.display='none'; // Hiding legend as we show (iso) in header now
354
  } else {
355
  const val = document.getElementById('asr-select-mod').value;
356
  document.getElementById('asr-title').innerText = `Results for Model: ${val}`;
357
+ legend.style.display='none'; rankInfo.style.display='none';
358
 
359
  const rows = DATA.asr.by_model[val];
360
+ html = `<table><thead><tr><th>Language (iso)</th><th>Family</th><th>WER (%) <i class="fa-solid fa-arrow-down" style="color:#d97706; font-size:10px"></i></th><th>CER (%) <i class="fa-solid fa-arrow-down" style="color:#d97706; font-size:10px"></i></th></tr></thead><tbody>`;
361
  rows.forEach(r => {
362
+ html += `<tr><td>${getIso(r.Language, r.ISO)}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
363
  });
364
  }
365
  div.innerHTML = html;
366
  }
367
 
368
+ // --- RENDER TTS ---
369
  function renderTTS() {
370
  const val = document.getElementById('tts-select').value;
371
+ document.getElementById('tts-title').innerText = `Model: ${val}`;
 
372
  const rows = DATA.tts[val];
373
  if(!rows) return;
374
 
375
  const metrics = [
376
+ {k:'wer', l:'WER(↓)', h:false}, {k:'mcd', l:'MCD(↓)', h:false},
377
+ {k:'logf0rmse', l:'LogF0RMSE(↓)', h:false}, {k:'speech_token_distance', l:'SpeechTokenDistance(↓)', h:false},
378
+ {k:'pesq', l:'PESQ(↑)', h:true}, {k:'utmos', l:'UTMOS(↑)', h:true},
379
+ {k:'speechbleu', l:'SpeechBLEU(↑)', h:true}, {k:'speechbert_score', l:'SpeechBERTScore(↑)', h:true}
380
  ];
381
 
382
  let sums={}, c=0;
383
  metrics.forEach(m=>sums[m.k]=0);
384
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
385
 
386
+ document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" +
387
+ metrics.map(m=> `${m.l.replace('(↓)','').replace('(↑)','')}: ${fmt(sums[m.k]/c)}`).join(" | ");
388
+
389
+ let html = `<table><thead><tr><th>Language (iso)</th>`;
390
+ metrics.forEach(m => html += `<th>${m.l}</th>`);
 
 
 
 
391
  html += `</tr></thead><tbody>`;
392
  rows.forEach(r => {
393
+ html += `<tr><td>${getIso(r.language, r.iso)}</td>`;
394
  metrics.forEach(m => html += `<td>${fmt(r[m.k])}</td>`);
395
  html += `</tr>`;
396
  });
397
  document.getElementById('tts-table').innerHTML = html;
398
  }
399
 
400
+ // --- RENDER SLID ---
401
  function renderSLID() {
402
+ // Sort by F1-macro (assuming Simba is the primary metric to sort by, or generally by Best Score)
403
+ // Here we sort alphabetically by language as it's a comparison view
404
+ DATA.slid.sort((a,b) => a.Language.localeCompare(b.Language));
405
+
406
+ let html = `<table><thead><tr><th>Language (iso)</th><th>MMS-LID-1024 (↑)</th><th>Simba-SLID (↑)</th></tr></thead><tbody>`;
407
  DATA.slid.forEach(r => {
408
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
409
  let mS = fmt(m), sS = fmt(s);
410
+ // Highlight winner
411
+ if(m > s) mS = `<b>${mS} πŸ₯‡</b>`;
412
+ else if(s > m) sS = `<b>${sS} πŸ₯‡</b>`;
413
+
414
+ html += `<tr><td>${getIso(r.Language)}</td><td>${mS}</td><td>${sS}</td></tr>`;
415
  });
416
  document.getElementById('slid-table').innerHTML = html;
417
  }
418
 
419
+ // --- UI ---
420
  window.setTab = (id) => {
 
421
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
422
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
423
+ document.getElementById(id).classList.add('active');
 
 
424
  document.getElementById(`tab-btn-${id}`).classList.add('active');
425
  };
426
 
427
  window.setASRMode = (m) => {
428
  asrMode = m;
429
+ const b1 = document.getElementById('btn-fam'), b2 = document.getElementById('btn-mod');
430
  document.getElementById('grp-fam').style.display = m==='family'?'inline-block':'none';
431
  document.getElementById('grp-mod').style.display = m==='model'?'inline-block':'none';
 
 
432
  if(m==='family') {
433
+ b1.className="action-btn bg-blue"; b1.style.color="white"; b1.style.background="";
434
+ b2.className="action-btn"; b2.style.background="#e2e8f0"; b2.style.color="#334155";
435
  } else {
436
+ b2.className="action-btn bg-blue"; b2.style.color="white"; b2.style.background="";
437
+ b1.className="action-btn"; b1.style.background="#e2e8f0"; b1.style.color="#334155";
438
  }
439
  renderASR();
440
  }
441
+
442
+ window.copyCitation = () => {
443
+ const text = document.getElementById('bibtex').innerText;
444
+ navigator.clipboard.writeText(text).then(() => {
445
+ const btn = document.querySelector('.copy-btn');
446
+ btn.innerHTML = '<i class="fa-solid fa-check"></i> Copied!';
447
+ setTimeout(() => btn.innerHTML = '<i class="fa-regular fa-copy"></i> Copy', 2000);
448
+ });
449
+ }
450
  </script>
451
  </body>
452
  </html>