elmadany commited on
Commit
8e652c5
·
verified ·
1 Parent(s): 4d3a843

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +329 -216
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>SimbaBench - Voice of a Continent</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">
@@ -13,118 +13,238 @@
13
 
14
  <style>
15
  :root {
16
- /* Brand Colors */
17
- --bg-deep: #1e1b4b;
18
- --text-yellow: #fbbf24;
19
  --text-white: #ffffff;
20
- --text-mute: #94a3b8;
21
 
22
- /* Gradients */
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-blue: linear-gradient(135deg, #3b82f6, #0ea5e9);
27
- --grad-green: linear-gradient(135deg, #22c55e, #10b981);
28
 
29
- --simba-navy: #0f172a;
30
- --border-gold: #dca02a;
 
 
 
 
31
  }
32
 
33
  body {
34
  background-color: var(--bg-deep);
35
  font-family: 'Inter', sans-serif;
36
- color: #334155;
37
- margin: 0;
38
- padding: 0;
39
  overflow-x: hidden;
40
  }
41
 
42
- /* --- VISIBILITY CLASSES (FIXED) --- */
43
- .view {
44
- display: none !important; /* Force hide by default */
45
- animation: fadeIn 0.4s ease-in-out;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
- .view.active {
48
- display: block !important; /* Force show if active */
 
 
 
 
 
 
49
  }
50
 
51
- /* --- WAVE ANIMATION --- */
52
- .wave-bg {
53
- position: absolute; top: 150px; left: 0; width: 100%; height: 300px;
54
- background: repeating-linear-gradient(90deg, transparent, transparent 10px, rgba(255, 255, 255, 0.05) 10px, rgba(255, 255, 255, 0.05) 12px);
55
- mask-image: linear-gradient(to right, transparent, black 20%, black 80%, transparent);
56
- z-index: 0; pointer-events: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
 
58
 
59
- /* --- NAVBAR --- */
60
- .navbar { padding: 20px 40px; display: flex; justify-content: space-between; align-items: center; z-index: 10; position: relative; }
61
- .nav-logo { color: white; font-family: 'Rubik', sans-serif; font-weight: 700; font-size: 20px; display: flex; align-items: center; gap: 10px; text-decoration: none; }
62
- .nav-logo img { height: 35px; }
63
- .nav-links { display: flex; gap: 30px; align-items: center; }
64
- .nav-links a { color: #cbd5e1; text-decoration: none; font-size: 14px; font-weight: 500; transition: color 0.2s; }
65
- .nav-links a:hover { color: white; }
66
- .nav-btn-action { background: var(--grad-blue); padding: 8px 20px; border-radius: 20px; color: white !important; font-weight: 600; box-shadow: 0 4px 10px rgba(59, 130, 246, 0.3); }
67
- .login-btn { background: var(--text-yellow); color: var(--bg-deep) !important; padding: 8px 20px; border-radius: 20px; font-weight: 700; }
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
- /* --- HERO --- */
70
- .hero { display: flex; align-items: center; justify-content: space-between; max-width: 1300px; margin: 40px auto 80px; padding: 0 40px; position: relative; z-index: 2; }
71
- .hero-content { flex: 1; max-width: 650px; }
72
- .conference-tag { background: white; display: inline-block; padding: 5px 15px; border-radius: 8px; margin-bottom: 20px; }
73
- .hero h1 { font-family: 'Rubik', sans-serif; font-size: 56px; font-weight: 800; color: white; margin: 0; line-height: 1.1; }
74
- .hero h2 { font-family: 'Rubik', sans-serif; font-size: 28px; font-weight: 700; color: var(--text-yellow); margin: 10px 0 20px; letter-spacing: 1px; }
75
- .hero p { color: var(--text-mute); font-size: 16px; line-height: 1.6; margin-bottom: 40px; max-width: 90%; }
 
 
 
 
76
 
77
- .hero-buttons { display: flex; flex-wrap: wrap; gap: 15px; }
78
- .h-btn { text-decoration: none; color: white; padding: 12px 24px; border-radius: 30px; font-family: 'Rubik', sans-serif; font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 8px; transition: transform 0.2s, box-shadow 0.2s; }
79
- .h-btn:hover { transform: translateY(-2px); filter: brightness(1.1); }
80
- .btn-paper { background: var(--grad-red); box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4); }
81
- .btn-pipeline { background: var(--grad-purple); box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4); }
82
- .btn-dataset { background: var(--grad-orange); box-shadow: 0 4px 15px rgba(249, 115, 22, 0.4); }
83
- .btn-models { background: var(--grad-blue); box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4); }
84
- .btn-leaderboard { background: var(--grad-green); box-shadow: 0 4px 15px rgba(34, 197, 94, 0.4); }
85
-
86
- .hero-visual { flex: 1; display: flex; justify-content: flex-end; position: relative; }
87
- .hero-visual img { max-width: 100%; height: auto; animation: fadeIn 1.5s ease-out; }
88
-
89
- /* --- LEADERBOARD SECTION --- */
90
- .leaderboard-section { background: #f8fafc; border-radius: 24px 24px 0 0; padding: 60px 40px; min-height: 100vh; }
91
- .lb-container { max-width: 1300px; margin: 0 auto; }
92
 
93
- .content-card { background: white; border-radius: 16px; padding: 30px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); margin-bottom: 40px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
- /* Tabs Style */
96
- .tabs { display: flex; gap: 10px; margin-bottom: 25px; border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; }
97
- .tab-btn {
98
- padding: 12px 24px; border-radius: 8px; border: none; background: transparent;
99
- font-weight: 600; color: #64748b; cursor: pointer; transition: 0.2s; font-size: 15px;
 
 
100
  }
101
- .tab-btn:hover { background: #f1f5f9; color: var(--simba-navy); }
102
- .tab-btn.active { background: var(--simba-navy); color: white; box-shadow: 0 4px 10px rgba(15, 23, 42, 0.2); }
 
 
 
103
 
104
  /* Controls */
105
- .controls { display: flex; gap: 15px; margin-bottom: 20px; align-items: center; flex-wrap: wrap; background: #f1f5f9; padding: 15px; border-radius: 12px; }
106
- select { padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1; cursor: pointer; font-size: 14px; min-width: 220px; }
107
-
108
- /* Tables */
109
- .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 8px; }
110
- table { width: 100%; border-collapse: collapse; font-size: 14px; background: white; }
111
- th { text-align: center; padding: 15px; color: var(--simba-navy); border-bottom: 2px solid #e2e8f0; vertical-align: bottom; background: #f8fafc; white-space: nowrap; }
112
- td { text-align: center; padding: 12px; border-bottom: 1px solid #f1f5f9; color: #334155; }
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
- th:first-child, td:first-child { text-align: left; font-weight: 700; position: sticky; left: 0; background: white; z-index: 2; border-right: 2px solid #f1f5f9; }
115
- thead th:first-child { z-index: 3; background: #f8fafc; }
 
 
 
 
 
116
  th:nth-child(2), td:nth-child(2) { text-align: left; }
117
 
118
- .arrow-up { color: #16a34a; } .arrow-down { color: #d97706; }
119
- @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
120
-
121
- /* Mobile Resp */
122
- @media (max-width: 900px) {
123
- .hero { flex-direction: column; text-align: center; }
124
- .hero-visual { margin-top: 40px; justify-content: center; }
125
- .hero-buttons { justify-content: center; }
126
- .nav-links { display: none; }
 
127
  }
 
 
 
 
 
128
  </style>
129
  </head>
130
  <body>
@@ -132,137 +252,140 @@
132
  <nav class="navbar">
133
  <a href="#" class="nav-logo">
134
  <img src="https://africa.dlnlp.ai/simba/images/favicon.png" alt="Logo">
135
- Voice of a Continent
 
 
 
136
  </a>
137
  <div class="nav-links">
138
- <a href="https://arxiv.org/abs/2510.17998" target="_blank">Paper</a>
139
- <a href="https://github.com/UBC-NLP/simba" target="_blank">Pipeline</a>
140
- <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank">Dataset</a>
141
- <a href="#">Models</a>
142
- <a href="#leaderboard-anchor" style="color:white; font-weight:bold;">Leaderboard</a>
143
- <a href="#">Citation</a>
144
- <a href="#" class="nav-btn-action">Submit New Results</a>
145
- <a href="#" class="login-btn">Login</a>
146
  </div>
147
  </nav>
148
 
149
  <div class="hero">
150
- <div class="wave-bg"></div>
151
  <div class="hero-content">
152
- <div class="conference-tag">
153
- <span style="font-weight:800; color:#cc0000;">EMNLP 2025</span>
154
- <span style="font-size:12px; color:#555;">Suzhou, China</span>
155
  </div>
 
156
  <h1>Voice of a Continent</h1>
157
  <h2>Mapping Africa’s Speech Technology</h2>
 
158
  <p>
159
  SimbaBench bridges the digital divide with a unified suite for African AI: the largest
160
  open-source speech benchmark covering 61 languages, best-in-class multilingual models,
161
- and an interactive leaderboard.
162
  </p>
163
- <div class="hero-buttons">
164
- <a href="https://arxiv.org/abs/2510.17998" target="_blank" class="h-btn btn-paper"><i class="fa-regular fa-file-pdf"></i> Read Paper</a>
165
- <a href="https://github.com/UBC-NLP/simba" target="_blank" class="h-btn btn-pipeline"><i class="fa-solid fa-code-branch"></i> Pipeline</a>
166
- <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank" class="h-btn btn-dataset"><i class="fa-solid fa-database"></i> Dataset</a>
167
- <a href="#" class="h-btn btn-models"><i class="fa-solid fa-bolt"></i> Models</a>
168
- <a href="#leaderboard-anchor" class="h-btn btn-leaderboard"><i class="fa-solid fa-trophy"></i> Leaderboard</a>
 
 
 
 
 
 
 
 
 
 
 
169
  </div>
170
  </div>
171
  <div class="hero-visual">
172
- <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Graphic">
173
  </div>
174
  </div>
175
 
176
- <div class="leaderboard-section" id="leaderboard-anchor">
177
- <div class="lb-container">
178
- <div class="content-card">
179
-
180
- <div id="loader" style="text-align:center; padding:40px; color:#64748b;">
181
- <i class="fa-solid fa-circle-notch fa-spin fa-2x"></i><br><br>Loading Leaderboard Data...
182
- </div>
183
-
184
- <div id="content" style="display:none;">
185
- <div class="tabs">
186
- <button class="tab-btn active" id="tab-btn-asr" onclick="setTab('asr')">Automatic Speech Recognition (ASR)</button>
187
- <button class="tab-btn" id="tab-btn-tts" onclick="setTab('tts')">Text-to-Speech (TTS)</button>
188
- <button class="tab-btn" id="tab-btn-slid" onclick="setTab('slid')">Spoken Language Identification (SLID)</button>
189
- </div>
190
 
191
- <div id="asr" class="view active">
192
- <div class="controls">
193
- <span style="font-weight:600; color:#0f172a;">View Mode:</span>
194
- <button id="btn-fam" class="h-btn btn-models" style="padding:6px 15px; font-size:12px; cursor:pointer;" onclick="setASRMode('family')">By Family</button>
195
- <button id="btn-mod" class="h-btn" style="background:#cbd5e1; color:#334155; padding:6px 15px; font-size:12px; cursor:pointer;" onclick="setASRMode('model')">By Model</button>
196
-
197
- <div style="flex-grow:1; text-align:right;">
198
- <div id="grp-fam" style="display:inline-block;">
199
- <label>Select Family: </label>
200
- <select id="asr-select-fam" onchange="renderASR()" style="padding:6px; border-radius:6px;"></select>
201
- </div>
202
- <div id="grp-mod" style="display:none;">
203
- <label>Select Model: </label>
204
- <select id="asr-select-mod" onchange="renderASR()" style="padding:6px; border-radius:6px;"></select>
205
- </div>
206
- </div>
207
- </div>
208
- <h3 id="asr-title" style="color:#d97706; font-size:18px;"></h3>
209
- <div id="asr-legend" style="background:#f1f5f9; padding:10px; font-size:13px; border-radius:6px; margin-bottom:15px; display:none;"></div>
210
- <div id="asr-table" class="table-wrap"></div>
211
- </div>
212
 
213
- <div id="tts" class="view">
214
- <div class="controls">
215
- <label>Select Model: </label>
216
- <select id="tts-select" onchange="renderTTS()" style="padding:6px; border-radius:6px; min-width:250px;"></select>
217
- </div>
218
- <h3 id="tts-title" style="color:#d97706; font-size:18px;"></h3>
219
- <div id="tts-summary" style="margin-bottom:15px; font-weight:500;"></div>
220
- <div id="tts-table" class="table-wrap"></div>
 
221
  </div>
 
 
 
 
 
 
222
 
223
- <div id="slid" class="view">
224
- <div id="slid-table" class="table-wrap"></div>
225
- </div>
 
226
  </div>
 
 
 
227
  </div>
228
 
229
- <div style="text-align:center; color:#64748b; margin-top:40px;">
230
- <p>&copy; 2025 Simba Benchmark. <a href="https://africa.dlnlp.ai/simba/" style="color:#d97706;">Official Website</a></p>
 
231
  </div>
232
  </div>
 
 
 
 
 
 
 
 
 
 
 
233
  </div>
234
 
235
- <script>
236
- // --- LOGIC ---
237
- const NAME_TO_ISO = {
238
- 'Afrikaans': 'afr', 'Akuapim-twi': 'ak', 'Amharic': 'amh', 'Asante-twi': 'tw',
239
- 'Bemba': 'bem', 'Hausa': 'hau', 'Igbo': 'ibo', 'Yoruba': 'yor', 'Zulu': 'zul',
240
- 'Swahili': 'swa', 'Somali': 'som', 'Wolof': 'wol', 'Xhosa': 'xho'
241
- };
242
 
 
243
  let DATA = null;
244
  let asrMode = 'family';
245
 
246
- // Load Data
247
  fetch('/api/data').then(r=>r.json()).then(d=>{
248
  DATA = d;
249
  document.getElementById('loader').style.display='none';
250
- document.getElementById('content').style.display='block';
251
  init();
252
- }).catch(e=>{
253
- document.getElementById('loader').innerHTML = `<span style="color:red">Failed to load data.</span>`;
254
  });
255
 
256
  function init() {
257
- populate('asr-select-fam', DATA.metadata.families);
258
- populate('asr-select-mod', DATA.metadata.models);
259
  populate('tts-select', DATA.metadata.tts_models);
260
-
261
- // Set Initial State Explicitly
262
- setTab('asr');
263
- renderASR();
264
- renderTTS();
265
- renderSLID();
266
  }
267
 
268
  function populate(id, list) {
@@ -270,41 +393,40 @@
270
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
271
  }
272
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
273
- function getIso(name) { return NAME_TO_ISO[name] || name.substring(0,3).toLowerCase(); }
274
 
275
- // --- RENDER ASR ---
276
  function renderASR() {
 
277
  const div = document.getElementById('asr-table');
278
  const legend = document.getElementById('asr-legend');
279
  let html='';
280
 
281
  if(asrMode==='family'){
282
- const val = document.getElementById('asr-select-fam').value;
283
- document.getElementById('asr-title').innerText = `Results for Family: ${val}`;
284
  const d = DATA.asr.by_family[val];
285
  if(!d) return;
286
 
 
287
  legend.style.display = 'block';
288
- legend.innerHTML = `<b>Codes:</b> ` + d.languages.map(l => `<span style="margin-right:10px"><b>${getIso(l)}</b>=${l}</span>`).join("");
289
 
290
- html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down arrow-down"></i></th>`;
291
- d.languages.forEach(l => html += `<th>${getIso(l)}</th>`);
292
  html += `</tr></thead><tbody>`;
293
 
294
  d.data.forEach(r => {
295
  html += `<tr><td>${r.Model}</td><td style="font-weight:bold; background:#f8fafc">${fmt(r.Avg_WER)} / ${fmt(r.Avg_CER)}</td>`;
296
  d.languages.forEach(l => {
297
- let w=r[`WER_${l}`], c=r[`CER_${l}`];
298
  html += `<td>${(w!=null)?fmt(w)+'/'+fmt(c):'-'}</td>`;
299
  });
300
  html += `</tr>`;
301
  });
302
  } else {
303
- const val = document.getElementById('asr-select-mod').value;
304
- document.getElementById('asr-title').innerText = `Results for Model: ${val}`;
305
  legend.style.display = 'none';
306
  const rows = DATA.asr.by_model[val];
307
- 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>`;
308
  rows.forEach(r => {
309
  html += `<tr><td>${r.Language}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
310
  });
@@ -312,32 +434,26 @@
312
  div.innerHTML = html;
313
  }
314
 
315
- // --- RENDER TTS ---
316
  function renderTTS() {
317
  const val = document.getElementById('tts-select').value;
318
- document.getElementById('tts-title').innerText = `Results for Model: ${val}`;
319
  const rows = DATA.tts[val];
320
  if(!rows) return;
321
 
322
  const metrics = [
323
- {k:'wer', l:'WER', h:false}, {k:'mcd', l:'MCD', h:false},
324
- {k:'logf0rmse', l:'LogF0', h:false}, {k:'speech_token_distance', l:'SpTokDist', h:false},
325
- {k:'pesq', l:'PESQ', h:true}, {k:'utmos', l:'UTMOS', h:true},
326
- {k:'speechbleu', l:'SpBLEU', h:true}, {k:'speechbert_score', l:'SpBERT', h:true}
327
  ];
328
 
329
  let sums={}, c=0;
330
  metrics.forEach(m=>sums[m.k]=0);
331
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
332
- let sumHTML = metrics.map(m => `<span>${m.l}: <b>${fmt(sums[m.k]/c)}</b></span>`).join(" | ");
333
- document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + sumHTML;
334
 
335
  let html = `<table><thead><tr><th>Language</th>`;
336
- metrics.forEach(m => {
337
- let icon = m.h ? 'up' : 'down';
338
- let col = m.h ? 'green' : '#d97706';
339
- html += `<th>${m.l} <i class="fa-solid fa-arrow-${icon}" style="color:${col}"></i></th>`;
340
- });
341
  html += `</tr></thead><tbody>`;
342
  rows.forEach(r => {
343
  html += `<tr><td>${r.language}</td>`;
@@ -347,41 +463,38 @@
347
  document.getElementById('tts-table').innerHTML = html;
348
  }
349
 
350
- // --- RENDER SLID ---
351
  function renderSLID() {
352
- 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>`;
353
  DATA.slid.forEach(r => {
354
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
355
- let mS = fmt(m), sS = fmt(s);
356
- if(m > s) mS = `<b style="color:green">${mS}</b>`; else if(s > m) sS = `<b style="color:green">${sS}</b>`;
357
- html += `<tr><td>${r.Language}</td><td>${mS}</td><td>${sS}</td></tr>`;
358
  });
359
  document.getElementById('slid-table').innerHTML = html;
360
  }
361
 
362
- // --- INTERACTION ---
363
- window.setTab = (id) => {
364
- // Hide all views
365
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
366
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
367
-
368
- // Show selected
369
- document.getElementById(id).classList.add('active');
370
- document.getElementById(`tab-btn-${id}`).classList.add('active');
371
  };
372
 
373
  window.setASRMode = (m) => {
374
  asrMode = m;
375
- document.getElementById('grp-fam').style.display = m==='family'?'inline-block':'none';
376
- document.getElementById('grp-mod').style.display = m==='model'?'inline-block':'none';
377
  const b1 = document.getElementById('btn-fam');
378
  const b2 = document.getElementById('btn-mod');
379
  if(m==='family') {
380
- b1.className = "h-btn btn-models"; b1.style.background = "var(--grad-blue)"; b1.style.color="white";
381
- b2.className = "h-btn"; b2.style.background = "#cbd5e1"; b2.style.color="#334155";
 
382
  } else {
383
- b2.className = "h-btn btn-models"; b2.style.background = "var(--grad-blue)"; b2.style.color="white";
384
- b1.className = "h-btn"; b1.style.background = "#cbd5e1"; b1.style.color="#334155";
 
385
  }
386
  renderASR();
387
  }
 
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">
 
13
 
14
  <style>
15
  :root {
16
+ --bg-deep: #1e1b4b; /* Deep Navy */
17
+ --bg-lighter: #262357; /* Slightly lighter for cards */
18
+ --text-yellow: #fbbf24; /* Brand Yellow */
19
  --text-white: #ffffff;
20
+ --text-mute: #94a3b8;
21
 
22
+ /* Buttons Gradients */
23
+ --grad-blue: linear-gradient(90deg, #3b82f6, #0ea5e9);
24
+ --grad-yellow: linear-gradient(90deg, #eab308, #ca8a04);
 
 
 
25
 
26
+ /* Hero Buttons */
27
+ --btn-red: #ef4444;
28
+ --btn-purple: #8b5cf6;
29
+ --btn-orange: #f97316;
30
+ --btn-blue: #3b82f6;
31
+ --btn-green: #22c55e;
32
  }
33
 
34
  body {
35
  background-color: var(--bg-deep);
36
  font-family: 'Inter', sans-serif;
37
+ color: var(--text-white);
38
+ margin: 0; padding: 0;
 
39
  overflow-x: hidden;
40
  }
41
 
42
+ a { text-decoration: none; transition: 0.2s; }
43
+
44
+ /* --- NAVBAR --- */
45
+ .navbar {
46
+ padding: 20px 40px;
47
+ display: flex;
48
+ justify-content: space-between;
49
+ align-items: center;
50
+ background: transparent;
51
+ z-index: 100;
52
+ position: relative;
53
+ }
54
+ .nav-logo {
55
+ color: white;
56
+ font-family: 'Rubik', sans-serif;
57
+ font-weight: 700;
58
+ font-size: 22px;
59
+ display: flex; align-items: center; gap: 12px;
60
+ }
61
+ .nav-logo img { height: 40px; }
62
+ .nav-text { display: flex; flex-direction: column; line-height: 1.1; }
63
+ .nav-text span:first-child { font-size: 14px; color: var(--text-yellow); }
64
+
65
+ .nav-links { display: flex; gap: 25px; align-items: center; }
66
+ .nav-link { color: #e2e8f0; font-size: 15px; font-weight: 500; }
67
+ .nav-link:hover { color: var(--text-yellow); }
68
+
69
+ .btn-submit {
70
+ background: var(--grad-blue);
71
+ color: white !important;
72
+ padding: 10px 24px;
73
+ border-radius: 30px;
74
+ font-weight: 600;
75
+ font-size: 14px;
76
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
77
  }
78
+ .btn-login {
79
+ background: var(--text-yellow);
80
+ color: var(--bg-deep) !important;
81
+ padding: 10px 24px;
82
+ border-radius: 30px;
83
+ font-weight: 700;
84
+ font-size: 14px;
85
+ box-shadow: 0 4px 12px rgba(251, 191, 36, 0.4);
86
  }
87
 
88
+ /* --- HERO --- */
89
+ .hero {
90
+ display: flex;
91
+ align-items: center;
92
+ max-width: 1400px;
93
+ margin: 60px auto 100px;
94
+ padding: 0 40px;
95
+ position: relative;
96
+ }
97
+
98
+ .hero-content { flex: 1; z-index: 2; }
99
+
100
+ .conf-badge {
101
+ background: white;
102
+ color: black;
103
+ display: inline-flex;
104
+ align-items: center;
105
+ gap: 10px;
106
+ padding: 6px 16px;
107
+ border-radius: 8px;
108
+ margin-bottom: 30px;
109
+ font-weight: 700;
110
+ font-size: 14px;
111
  }
112
+ .conf-badge i { color: #cc0000; font-size: 18px; }
113
 
114
+ .hero h1 {
115
+ font-family: 'Rubik', sans-serif;
116
+ font-size: 64px;
117
+ font-weight: 800;
118
+ margin: 0;
119
+ line-height: 1.1;
120
+ }
121
+ .hero h2 {
122
+ font-family: 'Rubik', sans-serif;
123
+ font-size: 32px;
124
+ color: var(--text-yellow);
125
+ margin: 15px 0 30px;
126
+ font-weight: 700;
127
+ }
128
+ .hero p {
129
+ color: #cbd5e1;
130
+ font-size: 18px;
131
+ line-height: 1.6;
132
+ max-width: 650px;
133
+ margin-bottom: 50px;
134
+ }
135
 
136
+ .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; }
137
+ .action-btn {
138
+ display: flex; align-items: center; gap: 8px;
139
+ padding: 12px 24px;
140
+ border-radius: 30px;
141
+ color: white;
142
+ font-weight: 600;
143
+ font-size: 15px;
144
+ transition: transform 0.2s;
145
+ }
146
+ .action-btn:hover { transform: translateY(-3px); color: white; }
147
 
148
+ .bg-red { background: var(--btn-red); box-shadow: 0 8px 20px rgba(239, 68, 68, 0.3); }
149
+ .bg-purple { background: var(--btn-purple); box-shadow: 0 8px 20px rgba(139, 92, 246, 0.3); }
150
+ .bg-orange { background: var(--btn-orange); box-shadow: 0 8px 20px rgba(249, 115, 22, 0.3); }
151
+ .bg-blue { background: var(--btn-blue); box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3); }
152
+ .bg-green { background: var(--btn-green); box-shadow: 0 8px 20px rgba(34, 197, 94, 0.3); }
153
+
154
+ .hero-visual {
155
+ flex: 1;
156
+ display: flex;
157
+ justify-content: flex-end;
158
+ position: relative;
159
+ }
160
+ .hero-visual img { max-width: 100%; height: auto; z-index: 1; }
 
 
161
 
162
+ /* Wave */
163
+ .wave-container {
164
+ position: absolute;
165
+ top: 20%;
166
+ left: 0;
167
+ right: 0;
168
+ height: 400px;
169
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQwIDMyMCI+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZmlsbC1vcGFjaXR5PSIwLjAzIiBkPSJNMCwxNjBMMzAsMTQ5LjNDNjAsMTM5LDEyMCwxMTcsMTgwLDExN0MyNDAsMTE3LDMwMCwxMzksMzYwLDE0NCxDNDIwLDE0OSw0ODAsMTM5LDU0MCwxMjIuN0M2MDAsMTA3LDY2MCw4NSw3MjAsODUuM0M3ODAsODUsODQwLDEwNyw5MDAsMTI4Qzk2MCwxNDksMTAyMCwxNzEsMTA4MCwxNzAuN0MxMTQwLDE3MSwxMjAwLDE0OSwxMjYwLDEzOC43QzEzMjAsMTI4LDEzODAsMTI4LDE0MTAsMTI4TDE0NDAsMTI4TDE0NDAsMzIwTDE0MTAsMzIwQzEzODAsMzIwLDEzMjAsMzIwLDEyNjAsMzIwQzEyMDAsMzIwLDExNDAsMzIwLDEwODAsMzIwQzEwMjAsMzIwLDk2MCwzMjAsOTAwLDMyMEM4NDAsMzIwLDc4MCwzMjAsNzIwLDMyMEM2NjAsMzIwLDYwMCwzMjAsNTQwLDMyMEM0ODAsMzIwLDQyMCwzMjAsMzYwLDMyMEMzMDAsMzIwLDI0MCwzMjAsMTgwLDMyMEMxMjAsMzIwLDYwLDMyMCwzMCwzMjBMMCwzMjBaIj48L3BhdGg+PC9zdmc+');
170
+ background-size: cover;
171
+ opacity: 0.5;
172
+ z-index: 0;
173
+ pointer-events: none;
174
+ }
175
+
176
+ /* --- MAIN CONTENT AREA --- */
177
+ .main-card {
178
+ background: white;
179
+ border-radius: 24px;
180
+ margin: 0 40px 60px;
181
+ padding: 40px;
182
+ color: #0f172a;
183
+ min-height: 600px;
184
+ }
185
 
186
+ .tabs { display: flex; gap: 15px; border-bottom: 2px solid #e2e8f0; margin-bottom: 30px; padding-bottom: 10px; }
187
+ .tab-btn {
188
+ background: none; border: none;
189
+ font-size: 16px; font-weight: 600; color: #64748b;
190
+ padding: 10px 20px; cursor: pointer;
191
+ transition: 0.3s;
192
+ border-radius: 8px;
193
  }
194
+ .tab-btn:hover { background: #f1f5f9; color: var(--bg-deep); }
195
+ .tab-btn.active { background: var(--bg-deep); color: white; }
196
+
197
+ .view { display: none; animation: fadeIn 0.4s; }
198
+ .view.active { display: block; }
199
 
200
  /* Controls */
201
+ .controls {
202
+ background: #f8fafc; padding: 20px; border-radius: 12px;
203
+ display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap;
204
+ }
205
+ select {
206
+ padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1;
207
+ font-size: 14px; min-width: 250px;
208
+ }
209
+
210
+ /* Table */
211
+ .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; }
212
+ table { width: 100%; border-collapse: collapse; font-size: 14px; }
213
+ th {
214
+ background: #f1f5f9; color: #0f172a; font-weight: 700;
215
+ padding: 16px; text-align: center; border-bottom: 2px solid #e2e8f0;
216
+ white-space: nowrap; vertical-align: middle;
217
+ }
218
+ td {
219
+ padding: 14px; border-bottom: 1px solid #e2e8f0; text-align: center; color: #334155;
220
+ }
221
+ tr:hover td { background: #f8fafc; }
222
 
223
+ /* Sticky First Column */
224
+ th:first-child, td:first-child {
225
+ position: sticky; left: 0; background: white; z-index: 2;
226
+ text-align: left; font-weight: 700; color: #0f172a;
227
+ border-right: 2px solid #f1f5f9;
228
+ }
229
+ thead th:first-child { background: #f1f5f9; z-index: 3; }
230
  th:nth-child(2), td:nth-child(2) { text-align: left; }
231
 
232
+ /* Citation Box */
233
+ .citation-box {
234
+ background: #0f172a;
235
+ color: #cbd5e1;
236
+ padding: 20px;
237
+ border-radius: 12px;
238
+ font-family: monospace;
239
+ white-space: pre-wrap;
240
+ margin-top: 20px;
241
+ text-align: left;
242
  }
243
+
244
+ /* Footer */
245
+ .footer { text-align: center; padding-bottom: 40px; color: #94a3b8; font-size: 14px; }
246
+
247
+ @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} }
248
  </style>
249
  </head>
250
  <body>
 
252
  <nav class="navbar">
253
  <a href="#" class="nav-logo">
254
  <img src="https://africa.dlnlp.ai/simba/images/favicon.png" alt="Logo">
255
+ <div class="nav-text">
256
+ <span>Voice of a</span>
257
+ Continent
258
+ </div>
259
  </a>
260
  <div class="nav-links">
261
+ <a href="https://arxiv.org/abs/2510.17998" class="nav-link">Paper</a>
262
+ <a href="https://github.com/UBC-NLP/simba" class="nav-link">Pipeline</a>
263
+ <a href="https://huggingface.co/datasets/UBC-NLP/simba" class="nav-link">Dataset</a>
264
+ <a href="#" class="nav-link">Models</a>
265
+ <a href="#" class="nav-link" style="color:var(--text-yellow)">Leaderboard</a>
266
+ <a href="#citation" class="nav-link">Citation</a>
267
+ <a href="#" class="btn-submit">Submit New Results</a>
268
+ <a href="#" class="btn-login">Login</a>
269
  </div>
270
  </nav>
271
 
272
  <div class="hero">
273
+ <div class="wave-container"></div>
274
  <div class="hero-content">
275
+ <div class="conf-badge">
276
+ <i class="fa-solid fa-location-dot"></i> EMNLP 2025 · Suzhou, China
 
277
  </div>
278
+
279
  <h1>Voice of a Continent</h1>
280
  <h2>Mapping Africa’s Speech Technology</h2>
281
+
282
  <p>
283
  SimbaBench bridges the digital divide with a unified suite for African AI: the largest
284
  open-source speech benchmark covering 61 languages, best-in-class multilingual models,
285
+ and an interactive leaderboard driving the future of speech technology.
286
  </p>
287
+
288
+ <div class="hero-actions">
289
+ <a href="https://arxiv.org/abs/2510.17998" target="_blank" class="action-btn bg-red">
290
+ <i class="fa-regular fa-file-pdf"></i> Read Paper
291
+ </a>
292
+ <a href="https://github.com/UBC-NLP/simba" target="_blank" class="action-btn bg-purple">
293
+ <i class="fa-solid fa-code-branch"></i> Pipeline
294
+ </a>
295
+ <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank" class="action-btn bg-orange">
296
+ <i class="fa-solid fa-database"></i> Dataset
297
+ </a>
298
+ <a href="#" class="action-btn bg-blue">
299
+ <i class="fa-solid fa-bolt"></i> Models
300
+ </a>
301
+ <a href="#board" class="action-btn bg-green">
302
+ <i class="fa-solid fa-trophy"></i> Leaderboard
303
+ </a>
304
  </div>
305
  </div>
306
  <div class="hero-visual">
307
+ <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Map">
308
  </div>
309
  </div>
310
 
311
+ <div class="main-card" id="board">
312
+
313
+ <div id="loader"><i class="fa-solid fa-circle-notch fa-spin fa-2x" style="color:#0f172a"></i><br>Loading Data...</div>
 
 
 
 
 
 
 
 
 
 
 
314
 
315
+ <div id="app-content" style="display:none;">
316
+ <div class="tabs">
317
+ <button class="tab-btn active" onclick="setTab('asr', this)">Automatic Speech Recognition (ASR)</button>
318
+ <button class="tab-btn" onclick="setTab('tts', this)">Text-to-Speech (TTS)</button>
319
+ <button class="tab-btn" onclick="setTab('slid', this)">Spoken Language Identification (SLID)</button>
320
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
+ <div id="asr" class="view active">
323
+ <div class="controls">
324
+ <span style="font-weight:700;">View Mode:</span>
325
+ <button id="btn-fam" class="action-btn bg-blue" style="padding:8px 16px; font-size:12px; box-shadow:none;" onclick="setASRMode('family')">By Family</button>
326
+ <button id="btn-mod" class="action-btn" style="padding:8px 16px; font-size:12px; background:#e2e8f0; color:#334155;" onclick="setASRMode('model')">By Model</button>
327
+
328
+ <div style="flex-grow:1; text-align:right;">
329
+ <span style="font-weight:600; margin-right:10px;">Select:</span>
330
+ <select id="asr-select" onchange="renderASR()"></select>
331
  </div>
332
+ </div>
333
+
334
+ <h3 id="asr-title" style="color:#d97706; margin-bottom:15px;"></h3>
335
+ <div id="asr-legend" style="margin-bottom:15px; font-size:13px; color:#64748b; line-height:1.6;"></div>
336
+ <div id="asr-table" class="table-wrap"></div>
337
+ </div>
338
 
339
+ <div id="tts" class="view">
340
+ <div class="controls">
341
+ <span style="font-weight:600;">Select Model:</span>
342
+ <select id="tts-select" onchange="renderTTS()"></select>
343
  </div>
344
+ <h3 id="tts-title" style="color:#d97706; margin-bottom:10px;"></h3>
345
+ <div id="tts-summary" style="margin-bottom:20px; font-weight:600; color:#0f172a;"></div>
346
+ <div id="tts-table" class="table-wrap"></div>
347
  </div>
348
 
349
+ <div id="slid" class="view">
350
+ <h3 style="color:#d97706;">Spoken Language Identification Leaderboard</h3>
351
+ <div id="slid-table" class="table-wrap"></div>
352
  </div>
353
  </div>
354
+
355
+ <div id="citation" style="margin-top:60px;">
356
+ <h3>Citation</h3>
357
+ <p>If you use Simba, please cite our <a href="https://africa.dlnlp.ai/sahara/" style="color:#d97706; font-weight:bold;">ACL2025 paper</a>.</p>
358
+ <div class="citation-box">@inproceedings{simba2025,
359
+ title={Simba: A Unified Benchmark for Speech Tasks in African Languages},
360
+ author={Put Authors Here},
361
+ booktitle={ACL},
362
+ year={2025}
363
+ }</div>
364
+ </div>
365
  </div>
366
 
367
+ <div class="footer">
368
+ <img src="https://africa.dlnlp.ai/sahara//img/sahara_web_sponsers.jpg" width="250" style="opacity:0.8; margin-bottom:15px;">
369
+ <br>
370
+ &copy; 2025 Simba Benchmark.
371
+ </div>
 
 
372
 
373
+ <script>
374
  let DATA = null;
375
  let asrMode = 'family';
376
 
377
+ // Fetch Data
378
  fetch('/api/data').then(r=>r.json()).then(d=>{
379
  DATA = d;
380
  document.getElementById('loader').style.display='none';
381
+ document.getElementById('app-content').style.display='block';
382
  init();
 
 
383
  });
384
 
385
  function init() {
386
+ populate('asr-select', DATA.metadata.families); // Default family
 
387
  populate('tts-select', DATA.metadata.tts_models);
388
+ renderASR(); renderTTS(); renderSLID();
 
 
 
 
 
389
  }
390
 
391
  function populate(id, list) {
 
393
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
394
  }
395
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
 
396
 
397
+ // --- ASR ---
398
  function renderASR() {
399
+ const val = document.getElementById('asr-select').value;
400
  const div = document.getElementById('asr-table');
401
  const legend = document.getElementById('asr-legend');
402
  let html='';
403
 
404
  if(asrMode==='family'){
405
+ document.getElementById('asr-title').innerText = `Family: ${val}`;
 
406
  const d = DATA.asr.by_family[val];
407
  if(!d) return;
408
 
409
+ // Use the ISO from data directly (languages is array of {name, iso})
410
  legend.style.display = 'block';
411
+ legend.innerHTML = `<b>Codes:</b> ` + d.languages.map(l => `<span style="margin-right:12px"><b>${l.iso}</b>=${l.name}</span>`).join("");
412
 
413
+ html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down" style="font-size:10px; color:#d97706"></i></th>`;
414
+ d.languages.forEach(l => html += `<th>${l.iso}</th>`);
415
  html += `</tr></thead><tbody>`;
416
 
417
  d.data.forEach(r => {
418
  html += `<tr><td>${r.Model}</td><td style="font-weight:bold; background:#f8fafc">${fmt(r.Avg_WER)} / ${fmt(r.Avg_CER)}</td>`;
419
  d.languages.forEach(l => {
420
+ let w=r[`WER_${l.name}`], c=r[`CER_${l.name}`];
421
  html += `<td>${(w!=null)?fmt(w)+'/'+fmt(c):'-'}</td>`;
422
  });
423
  html += `</tr>`;
424
  });
425
  } else {
426
+ document.getElementById('asr-title').innerText = `Model: ${val}`;
 
427
  legend.style.display = 'none';
428
  const rows = DATA.asr.by_model[val];
429
+ html = `<table><thead><tr><th>Language</th><th>Family</th><th>WER (%)</th><th>CER (%)</th></tr></thead><tbody>`;
430
  rows.forEach(r => {
431
  html += `<tr><td>${r.Language}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
432
  });
 
434
  div.innerHTML = html;
435
  }
436
 
437
+ // --- TTS ---
438
  function renderTTS() {
439
  const val = document.getElementById('tts-select').value;
440
+ document.getElementById('tts-title').innerText = `Model: ${val}`;
441
  const rows = DATA.tts[val];
442
  if(!rows) return;
443
 
444
  const metrics = [
445
+ {k:'wer', l:'WER', h:false}, {k:'mcd', l:'MCD', h:false}, {k:'logf0rmse', l:'LogF0', h:false},
446
+ {k:'pesq', l:'PESQ', h:true}, {k:'utmos', l:'UTMOS', h:true}, {k:'speechbleu', l:'BLEU', h:true}
 
 
447
  ];
448
 
449
  let sums={}, c=0;
450
  metrics.forEach(m=>sums[m.k]=0);
451
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
452
+ let sumH = metrics.map(m=> `${m.l}: <b>${fmt(sums[m.k]/c)}</b>`).join(" | ");
453
+ document.getElementById('tts-summary').innerHTML = "Averages: " + sumH;
454
 
455
  let html = `<table><thead><tr><th>Language</th>`;
456
+ metrics.forEach(m => html += `<th>${m.l} <i class="fa-solid fa-arrow-${m.h?'up':'down'}" style="color:${m.h?'green':'orange'};font-size:10px"></i></th>`);
 
 
 
 
457
  html += `</tr></thead><tbody>`;
458
  rows.forEach(r => {
459
  html += `<tr><td>${r.language}</td>`;
 
463
  document.getElementById('tts-table').innerHTML = html;
464
  }
465
 
466
+ // --- SLID ---
467
  function renderSLID() {
468
+ let html = `<table><thead><tr><th>Language</th><th>MMS</th><th>Simba</th></tr></thead><tbody>`;
469
  DATA.slid.forEach(r => {
470
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
471
+ html += `<tr><td>${r.Language}</td>
472
+ <td style="color:${m>s?'green':'#333'}; font-weight:${m>s?'bold':'normal'}">${fmt(m)}</td>
473
+ <td style="color:${s>m?'green':'#333'}; font-weight:${s>m?'bold':'normal'}">${fmt(s)}</td></tr>`;
474
  });
475
  document.getElementById('slid-table').innerHTML = html;
476
  }
477
 
478
+ // --- UI ---
479
+ window.setTab = (id, btn) => {
 
480
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
481
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
482
+ document.getElementById(id).classList.add('active');
483
+ btn.classList.add('active');
 
 
484
  };
485
 
486
  window.setASRMode = (m) => {
487
  asrMode = m;
 
 
488
  const b1 = document.getElementById('btn-fam');
489
  const b2 = document.getElementById('btn-mod');
490
  if(m==='family') {
491
+ b1.className="action-btn bg-blue"; b1.style.color="white"; b1.style.background="";
492
+ b2.className="action-btn"; b2.style.background="#e2e8f0"; b2.style.color="#334155";
493
+ populate('asr-select', DATA.metadata.families);
494
  } else {
495
+ b2.className="action-btn bg-blue"; b2.style.color="white"; b2.style.background="";
496
+ b1.className="action-btn"; b1.style.background="#e2e8f0"; b1.style.color="#334155";
497
+ populate('asr-select', DATA.metadata.models);
498
  }
499
  renderASR();
500
  }