SeaWolf-AI commited on
Commit
42114bb
Β·
verified Β·
1 Parent(s): b3eab56

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +144 -366
index.html CHANGED
@@ -7,291 +7,115 @@
7
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
8
  <style>
9
  * { margin: 0; padding: 0; box-sizing: border-box; }
10
-
11
- body {
12
- font-family: 'Plus Jakarta Sans', -apple-system, sans-serif;
13
- background: linear-gradient(135deg, #FFF8F0 0%, #FFF0E6 30%, #F0F4FF 70%, #F8F0FF 100%);
14
- min-height: 100vh;
15
- color: #2D2B3D;
16
- }
17
-
18
- .bg-orbs {
19
- position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0;
20
- }
21
- .orb {
22
- position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0.15;
23
- animation: float 20s ease-in-out infinite;
24
- }
25
  .orb-1 { width: 400px; height: 400px; background: #FF6B35; top: -100px; right: -100px; }
26
  .orb-2 { width: 350px; height: 350px; background: #7C5CFC; bottom: -50px; left: -50px; animation-delay: -7s; }
27
  .orb-3 { width: 300px; height: 300px; background: #00C9A7; top: 50%; left: 40%; animation-delay: -14s; }
28
-
29
- @keyframes float {
30
- 0%, 100% { transform: translate(0, 0) scale(1); }
31
- 33% { transform: translate(30px, -30px) scale(1.05); }
32
- 66% { transform: translate(-20px, 20px) scale(0.95); }
33
- }
34
-
35
- .container {
36
- max-width: 800px; margin: 0 auto; padding: 32px 20px; position: relative; z-index: 1;
37
- }
38
-
39
- .header {
40
- text-align: center; margin-bottom: 36px;
41
- }
42
- .badge-link {
43
- text-decoration: none;
44
- }
45
- .badge {
46
- display: inline-flex; align-items: center; gap: 6px;
47
- background: linear-gradient(135deg, #FF6B35, #FF8F5E);
48
- color: white; font-size: 11px; font-weight: 700; letter-spacing: 1.5px;
49
- padding: 6px 16px; border-radius: 20px; text-transform: uppercase;
50
- margin-bottom: 16px; box-shadow: 0 4px 15px rgba(255,107,53,0.3);
51
- transition: transform 0.2s, box-shadow 0.2s; cursor: pointer;
52
- }
53
- .badge:hover {
54
- transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255,107,53,0.4);
55
- }
56
- .header h1 {
57
- font-size: 36px; font-weight: 800; line-height: 1.2;
58
- background: linear-gradient(135deg, #2D2B3D 0%, #7C5CFC 50%, #FF6B35 100%);
59
- -webkit-background-clip: text; -webkit-text-fill-color: transparent;
60
- margin-bottom: 12px;
61
- }
62
- .header p {
63
- font-size: 16px; color: #6B6880; max-width: 600px; margin: 0 auto; line-height: 1.6;
64
- }
65
- .header-links {
66
- display: flex; justify-content: center; gap: 12px; margin-top: 16px; flex-wrap: wrap;
67
- }
68
- .header-links a {
69
- display: inline-flex; align-items: center; gap: 6px;
70
- padding: 8px 18px; border-radius: 12px; font-size: 13px; font-weight: 600;
71
- text-decoration: none; transition: all 0.2s;
72
- }
73
- .link-model {
74
- background: #7C5CFC; color: white;
75
- box-shadow: 0 3px 12px rgba(124,92,252,0.3);
76
- }
77
  .link-model:hover { transform: translateY(-1px); box-shadow: 0 5px 18px rgba(124,92,252,0.4); }
78
- .link-blog {
79
- background: white; color: #7C5CFC; border: 2px solid #E8E6F0;
80
- }
81
  .link-blog:hover { border-color: #7C5CFC; transform: translateY(-1px); }
82
- .link-article {
83
- background: white; color: #FF6B35; border: 2px solid #FFE0D0;
84
- }
85
  .link-article:hover { border-color: #FF6B35; transform: translateY(-1px); }
86
-
87
- .card {
88
- background: rgba(255,255,255,0.85); backdrop-filter: blur(20px);
89
- border: 1px solid rgba(255,255,255,0.9);
90
- border-radius: 24px; padding: 32px;
91
- box-shadow: 0 8px 40px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04);
92
- margin-bottom: 20px;
93
- }
94
-
95
  .input-group { margin-bottom: 24px; }
96
- .input-group label {
97
- display: block; font-size: 13px; font-weight: 700;
98
- color: #4A4861; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px;
99
- }
100
- textarea {
101
- width: 100%; padding: 16px 20px; border: 2px solid #E8E6F0;
102
- border-radius: 16px; font-family: inherit; font-size: 16px;
103
- resize: vertical; min-height: 80px; transition: all 0.3s;
104
- background: #FAFAFE; color: #2D2B3D;
105
- }
106
- textarea:focus {
107
- outline: none; border-color: #7C5CFC;
108
- box-shadow: 0 0 0 4px rgba(124,92,252,0.1);
109
- background: white;
110
- }
111
-
112
- .blend-control {
113
- background: linear-gradient(135deg, #F8F5FF, #FFF5F0);
114
- border-radius: 16px; padding: 24px; margin-bottom: 24px;
115
- border: 1px solid rgba(124,92,252,0.1);
116
- }
117
- .blend-header {
118
- display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;
119
- }
120
- .blend-value {
121
- font-size: 28px; font-weight: 800; color: #7C5CFC;
122
- font-family: 'JetBrains Mono', monospace;
123
- }
124
- .blend-label { font-size: 12px; color: #8B87A0; margin-top: 2px; }
125
-
126
- input[type="range"] {
127
- -webkit-appearance: none; width: 100%; height: 8px;
128
- border-radius: 4px; outline: none;
129
- background: linear-gradient(90deg, #E8E6F0 0%, #7C5CFC 50%, #FF6B35 100%);
130
- }
131
- input[type="range"]::-webkit-slider-thumb {
132
- -webkit-appearance: none; width: 28px; height: 28px; border-radius: 50%;
133
- background: white; cursor: pointer;
134
- box-shadow: 0 2px 10px rgba(124,92,252,0.4), 0 0 0 3px #7C5CFC;
135
- transition: transform 0.2s;
136
- }
137
- input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.15); }
138
-
139
- .blend-tags {
140
- display: flex; justify-content: space-between; margin-top: 8px;
141
- font-size: 11px; color: #A09CB0;
142
- }
143
-
144
- .presets {
145
- display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px;
146
- }
147
- .preset-btn {
148
- padding: 10px 16px; border: 2px solid #E8E6F0; border-radius: 12px;
149
- background: white; cursor: pointer; font-size: 13px; font-weight: 600;
150
- color: #6B6880; transition: all 0.2s; flex: 1; min-width: 100px; text-align: center;
151
- }
152
  .preset-btn:hover { border-color: #7C5CFC; color: #7C5CFC; transform: translateY(-1px); }
153
  .preset-btn.active { border-color: #7C5CFC; background: #7C5CFC; color: white; }
154
-
155
- .gen-btn {
156
- width: 100%; padding: 18px; border: none; border-radius: 16px;
157
- background: linear-gradient(135deg, #7C5CFC, #9B7AFF);
158
- color: white; font-size: 17px; font-weight: 700; cursor: pointer;
159
- transition: all 0.3s; font-family: inherit;
160
- box-shadow: 0 6px 25px rgba(124,92,252,0.35);
161
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  .gen-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(124,92,252,0.45); }
163
- .gen-btn:active { transform: translateY(0); }
164
- .gen-btn:disabled {
165
- background: #C5C0D8; cursor: not-allowed; box-shadow: none; transform: none;
166
- }
167
-
168
  .loading { display: none; text-align: center; padding: 40px; }
169
  .loading.show { display: block; }
170
- .spinner {
171
- width: 48px; height: 48px; border: 4px solid #E8E6F0;
172
- border-top-color: #7C5CFC; border-radius: 50%;
173
- animation: spin 0.8s linear infinite; margin: 0 auto 16px;
174
- }
175
  @keyframes spin { to { transform: rotate(360deg); } }
176
  .loading-text { font-size: 14px; color: #8B87A0; }
177
-
178
  .result { display: none; margin-top: 24px; }
179
  .result.show { display: block; }
180
- .audio-card {
181
- background: linear-gradient(135deg, #F0FFF4, #F0F8FF);
182
- border: 2px solid #00C9A7; border-radius: 16px; padding: 24px;
183
- }
184
- .audio-card h3 {
185
- font-size: 15px; font-weight: 700; color: #00896B; margin-bottom: 12px;
186
- display: flex; align-items: center; gap: 8px;
187
- }
188
  audio { width: 100%; margin-top: 8px; border-radius: 8px; }
189
  .meta { font-size: 12px; color: #6B8F80; margin-top: 8px; }
190
-
191
- .info-grid {
192
- display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
193
- gap: 12px; margin-bottom: 20px;
194
- }
195
- .info-item {
196
- background: rgba(255,255,255,0.7); border-radius: 16px; padding: 20px;
197
- text-align: center; border: 1px solid rgba(0,0,0,0.04);
198
- }
199
  .info-item .icon { font-size: 28px; margin-bottom: 8px; }
200
  .info-item .val { font-size: 22px; font-weight: 800; color: #7C5CFC; }
201
  .info-item .lbl { font-size: 11px; color: #8B87A0; text-transform: uppercase; letter-spacing: 0.5px; margin-top: 4px; }
202
-
203
- .steps { display: grid; gap: 12px; }
204
- .step {
205
- display: flex; gap: 16px; align-items: flex-start;
206
- background: rgba(255,255,255,0.6); border-radius: 14px; padding: 16px;
207
- border: 1px solid rgba(0,0,0,0.03);
208
- }
209
- .step-num {
210
- width: 32px; height: 32px; border-radius: 10px;
211
- background: linear-gradient(135deg, #7C5CFC, #9B7AFF);
212
- color: white; font-weight: 800; font-size: 14px;
213
- display: flex; align-items: center; justify-content: center; flex-shrink: 0;
214
- }
215
- .step-text h4 { font-size: 14px; font-weight: 700; color: #2D2B3D; }
216
- .step-text p { font-size: 12px; color: #8B87A0; margin-top: 4px; line-height: 1.5; }
217
-
218
- .arch-box {
219
- background: #2D2B3D; border-radius: 14px; padding: 20px; margin-top: 12px;
220
- font-family: 'JetBrains Mono', monospace; font-size: 12px;
221
- color: #B8B5C8; line-height: 1.8; overflow-x: auto;
222
- }
223
- .arch-box .hl { color: #FF6B35; font-weight: 600; }
224
- .arch-box .ok { color: #00C9A7; }
225
- .arch-box .dim { color: #6B6880; }
226
-
227
- .footer {
228
- text-align: center; padding: 32px 0; font-size: 12px; color: #A09CB0;
229
- }
230
  .footer a { color: #7C5CFC; text-decoration: none; font-weight: 600; }
231
- .footer a:hover { text-decoration: underline; }
232
-
233
- .error {
234
- background: #FFF0F0; border: 2px solid #FF6B6B; border-radius: 14px;
235
- padding: 16px; color: #CC3333; font-size: 13px; margin-top: 16px; display: none;
236
- }
237
- .error.show { display: block; }
238
  </style>
239
  </head>
240
  <body>
241
-
242
- <div class="bg-orbs">
243
- <div class="orb orb-1"></div>
244
- <div class="orb orb-2"></div>
245
- <div class="orb orb-3"></div>
246
- </div>
247
-
248
  <div class="container">
249
-
250
- <!-- Header -->
251
  <div class="header">
252
  <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="badge-link">
253
- <div class="badge">🧬 World's First Cross-Modal FFN Transfer Β· πŸ€— Model Card</div>
254
  </a>
255
- <h1>Darwin-TTS-1.7B-Cross</h1>
256
- <p>Blend an LLM's language understanding into a TTS model β€” enhancing emotional expressiveness without any training. Just weight-space arithmetic.</p>
257
  <div class="header-links">
258
- <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-model">πŸ€— Model Weights</a>
259
- <a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank" class="link-blog">πŸ“„ Blog Post</a>
260
- <a href="https://huggingface.co/spaces/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-article">πŸš€ This Space</a>
261
  </div>
262
  </div>
263
-
264
- <!-- Stats -->
265
  <div class="info-grid">
266
- <div class="info-item">
267
- <div class="icon">🎯</div>
268
- <div class="val">84</div>
269
- <div class="lbl">FFN Tensors Blended</div>
270
- </div>
271
- <div class="info-item">
272
- <div class="icon">βœ…</div>
273
- <div class="val">100%</div>
274
- <div class="lbl">Shape Match</div>
275
- </div>
276
- <div class="info-item">
277
- <div class="icon">⚑</div>
278
- <div class="val">0</div>
279
- <div class="lbl">Training Required</div>
280
- </div>
281
- <div class="info-item">
282
- <div class="icon">πŸ•</div>
283
- <div class="val">&lt;2m</div>
284
- <div class="lbl">Build Time</div>
285
- </div>
286
  </div>
287
-
288
- <!-- Main Card -->
289
  <div class="card">
290
  <div class="input-group">
291
  <label>πŸ’¬ Text to Speak</label>
292
  <textarea id="textInput" placeholder="Enter text in Korean, English, or other supported languages...">μ•ˆλ…•ν•˜μ„Έμš”, μ €λŠ” λ‹€μœˆ 인곡지λŠ₯μž…λ‹ˆλ‹€. λ§Œλ‚˜μ„œ λ°˜κ°‘μŠ΅λ‹ˆλ‹€!</textarea>
293
  </div>
294
-
295
  <div class="input-group">
296
  <label>πŸ“ Quick Examples</label>
297
  <div class="presets">
@@ -301,39 +125,34 @@ audio { width: 100%; margin-top: 8px; border-radius: 8px; }
301
  <button class="preset-btn" onclick="setPreset('였늘 날씨가 정말 μ’‹λ„€μš”. μ‚°μ±…ν•˜κΈ° λ”± 쒋은 λ‚ μ΄μ—μš”.')">🌀️ 일상</button>
302
  </div>
303
  </div>
304
-
305
- <div class="blend-control">
306
- <div class="blend-header">
307
- <div>
308
- <label style="font-size:13px;font-weight:700;color:#4A4861;">🧬 LLM Blend Ratio</label>
309
- <div class="blend-label" id="blendDesc">β˜… Recommended β€” emotion appears</div>
310
  </div>
311
- <div>
312
- <div class="blend-value" id="blendValue">3%</div>
313
- </div>
314
- </div>
315
- <input type="range" id="blendSlider" min="0" max="5" step="1" value="3" oninput="updateBlend(this.value)">
316
- <div class="blend-tags">
317
- <span>0% Original</span>
318
- <span>1%</span>
319
- <span>2%</span>
320
- <span>3% β˜…</span>
321
- <span>4%</span>
322
- <span>5% β˜…β˜…</span>
323
  </div>
324
  </div>
325
-
326
- <button class="gen-btn" id="genBtn" onclick="generate()">
327
- πŸ”Š Generate Speech
328
- </button>
329
-
330
- <div class="loading" id="loading">
331
- <div class="spinner"></div>
332
- <div class="loading-text">🧬 Blending LLM intelligence into TTS...<br>This may take 30~60 seconds on first run.</div>
 
 
 
 
333
  </div>
334
-
 
335
  <div class="error" id="error"></div>
336
-
337
  <div class="result" id="result">
338
  <div class="audio-card">
339
  <h3>✨ Darwin-TTS Output</h3>
@@ -342,132 +161,91 @@ audio { width: 100%; margin-top: 8px; border-radius: 8px; }
342
  </div>
343
  </div>
344
  </div>
345
-
346
- <!-- How it works -->
347
  <div class="card">
348
  <h2 style="font-size:20px;font-weight:800;margin-bottom:20px;color:#2D2B3D;">πŸ”¬ How It Works</h2>
349
- <div class="steps">
350
- <div class="step">
351
- <div class="step-num">1</div>
352
- <div class="step-text">
353
- <h4>Load Qwen3-TTS-1.7B</h4>
354
- <p>A 4-module TTS model: talker (LM backbone) + code_predictor + speech_tokenizer + decoder</p>
355
- </div>
356
- </div>
357
- <div class="step">
358
- <div class="step-num">2</div>
359
- <div class="step-text">
360
- <h4>Extract Qwen3-1.7B LLM FFN</h4>
361
- <p>84 FFN tensors (gate_proj, up_proj, down_proj Γ— 28 layers) β€” identical shape to TTS talker</p>
362
- </div>
363
- </div>
364
- <div class="step">
365
- <div class="step-num">3</div>
366
- <div class="step-text">
367
- <h4>Blend with lerp (Ξ±=3%)</h4>
368
- <p><code>p.lerp_(llm_weight, 0.03)</code> β€” 97% TTS + 3% LLM language understanding</p>
369
- </div>
370
- </div>
371
- <div class="step">
372
- <div class="step-num">4</div>
373
- <div class="step-text">
374
- <h4>Generate Speech</h4>
375
- <p>The TTS model now "understands" text slightly better, producing more emotionally expressive speech</p>
376
- </div>
377
- </div>
378
- </div>
379
-
380
  <div class="arch-box">
381
- <span class="dim">Qwen3-TTS-1.7B (4-module):</span><br>
382
- β”œβ”€β”€ <span class="hl">talker</span> (28L Qwen3 LM) ← <span class="ok">FFN blended Ξ±=3%</span><br>
383
  β”œβ”€β”€ code_predictor (5L) ← <span class="dim">untouched</span><br>
384
  β”œβ”€β”€ speech_tokenizer ← <span class="dim">untouched</span><br>
385
  └── encoder/decoder ← <span class="dim">untouched</span><br>
386
  <br>
387
- <span class="dim">Key mapping:</span> talker.model.layers.N ↔ model.layers.N <span class="ok">(1:1)</span>
388
  </div>
389
  </div>
390
-
391
- <!-- Footer -->
392
  <div class="footer">
393
- <p>Built by <a href="https://huggingface.co/FINAL-Bench" target="_blank">VIDRAFT</a> (λΉ„λ“œλž˜ν”„νŠΈ) Β· Darwin Evolutionary Merge Framework</p>
394
  <p style="margin-top:6px;">
395
- <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank">πŸ€— Model</a> Β·
396
- <a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank">πŸ“„ Blog</a> Β·
397
- <a href="https://huggingface.co/spaces/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank">πŸš€ Space</a> Β·
398
- Apache 2.0
399
  </p>
400
  </div>
401
-
402
  </div>
403
-
404
  <script>
405
- function updateBlend(val) {
406
- const v = parseInt(val);
407
- document.getElementById('blendValue').textContent = v + '%';
408
- const descs = {
409
- 0: 'Original Qwen3-TTS (no blending)',
410
- 1: 'Subtle change β€” barely noticeable',
411
- 2: 'Light blend β€” minor tonal shift',
412
- 3: 'β˜… Recommended β€” emotion appears',
413
- 4: 'Strong blend β€” noticeable emotion',
414
- 5: 'β˜…β˜… Maximum stable β€” emotion intensified'
415
- };
416
- document.getElementById('blendDesc').textContent = descs[v] || '';
417
  }
418
-
419
  function setPreset(text) {
420
  document.getElementById('textInput').value = text;
421
  document.querySelectorAll('.preset-btn').forEach(b => b.classList.remove('active'));
422
  event.target.classList.add('active');
423
  }
424
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  async function generate() {
426
  const text = document.getElementById('textInput').value.trim();
427
- const alpha = parseInt(document.getElementById('blendSlider').value);
428
-
429
  if (!text) return;
430
-
431
  const btn = document.getElementById('genBtn');
432
  const loading = document.getElementById('loading');
433
  const result = document.getElementById('result');
434
  const error = document.getElementById('error');
435
-
436
- btn.disabled = true;
437
- btn.textContent = '⏳ Generating...';
438
- loading.classList.add('show');
439
- result.classList.remove('show');
440
- error.classList.remove('show');
441
-
442
  try {
443
- const resp = await fetch('/synthesize', {
444
- method: 'POST',
445
- headers: { 'Content-Type': 'application/json' },
446
- body: JSON.stringify({ text, alpha })
447
- });
448
-
449
- if (!resp.ok) {
450
- const err = await resp.json();
451
- throw new Error(err.detail || 'Generation failed');
452
- }
453
-
454
  const blob = await resp.blob();
455
  const url = URL.createObjectURL(blob);
456
  const dur = resp.headers.get('X-Duration') || '?';
457
-
458
  document.getElementById('audioPlayer').src = url;
459
- document.getElementById('audioMeta').textContent =
460
- `Duration: ${dur}s Β· Blend: ${alpha}% Β· Model: Darwin-TTS-1.7B-Cross`;
461
  result.classList.add('show');
462
-
463
- } catch (e) {
464
- error.textContent = '❌ ' + e.message;
465
- error.classList.add('show');
466
- } finally {
467
- btn.disabled = false;
468
- btn.textContent = 'πŸ”Š Generate Speech';
469
- loading.classList.remove('show');
470
- }
471
  }
472
  </script>
473
  </body>
 
7
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
8
  <style>
9
  * { margin: 0; padding: 0; box-sizing: border-box; }
10
+ body { font-family: 'Plus Jakarta Sans', -apple-system, sans-serif; background: linear-gradient(135deg, #FFF8F0 0%, #FFF0E6 30%, #F0F4FF 70%, #F8F0FF 100%); min-height: 100vh; color: #2D2B3D; }
11
+ .bg-orbs { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; }
12
+ .orb { position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0.15; animation: float 20s ease-in-out infinite; }
 
 
 
 
 
 
 
 
 
 
 
 
13
  .orb-1 { width: 400px; height: 400px; background: #FF6B35; top: -100px; right: -100px; }
14
  .orb-2 { width: 350px; height: 350px; background: #7C5CFC; bottom: -50px; left: -50px; animation-delay: -7s; }
15
  .orb-3 { width: 300px; height: 300px; background: #00C9A7; top: 50%; left: 40%; animation-delay: -14s; }
16
+ @keyframes float { 0%, 100% { transform: translate(0, 0) scale(1); } 33% { transform: translate(30px, -30px) scale(1.05); } 66% { transform: translate(-20px, 20px) scale(0.95); } }
17
+ .container { max-width: 800px; margin: 0 auto; padding: 32px 20px; position: relative; z-index: 1; }
18
+ .header { text-align: center; margin-bottom: 36px; }
19
+ .badge-link { text-decoration: none; }
20
+ .badge { display: inline-flex; align-items: center; gap: 6px; background: linear-gradient(135deg, #FF6B35, #FF8F5E); color: white; font-size: 11px; font-weight: 700; letter-spacing: 1.5px; padding: 6px 16px; border-radius: 20px; text-transform: uppercase; margin-bottom: 16px; box-shadow: 0 4px 15px rgba(255,107,53,0.3); transition: transform 0.2s, box-shadow 0.2s; cursor: pointer; }
21
+ .badge:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255,107,53,0.4); }
22
+ .header h1 { font-size: 36px; font-weight: 800; line-height: 1.2; background: linear-gradient(135deg, #2D2B3D 0%, #7C5CFC 50%, #FF6B35 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 12px; }
23
+ .header p { font-size: 16px; color: #6B6880; max-width: 600px; margin: 0 auto; line-height: 1.6; }
24
+ .header-links { display: flex; justify-content: center; gap: 12px; margin-top: 16px; flex-wrap: wrap; }
25
+ .header-links a { display: inline-flex; align-items: center; gap: 6px; padding: 8px 18px; border-radius: 12px; font-size: 13px; font-weight: 600; text-decoration: none; transition: all 0.2s; }
26
+ .link-model { background: #7C5CFC; color: white; box-shadow: 0 3px 12px rgba(124,92,252,0.3); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  .link-model:hover { transform: translateY(-1px); box-shadow: 0 5px 18px rgba(124,92,252,0.4); }
28
+ .link-blog { background: white; color: #7C5CFC; border: 2px solid #E8E6F0; }
 
 
29
  .link-blog:hover { border-color: #7C5CFC; transform: translateY(-1px); }
30
+ .link-article { background: white; color: #FF6B35; border: 2px solid #FFE0D0; }
 
 
31
  .link-article:hover { border-color: #FF6B35; transform: translateY(-1px); }
32
+ .card { background: rgba(255,255,255,0.85); backdrop-filter: blur(20px); border: 1px solid rgba(255,255,255,0.9); border-radius: 24px; padding: 32px; box-shadow: 0 8px 40px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04); margin-bottom: 20px; }
 
 
 
 
 
 
 
 
33
  .input-group { margin-bottom: 24px; }
34
+ .input-group label { display: block; font-size: 13px; font-weight: 700; color: #4A4861; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
35
+ textarea { width: 100%; padding: 16px 20px; border: 2px solid #E8E6F0; border-radius: 16px; font-family: inherit; font-size: 16px; resize: vertical; min-height: 80px; transition: all 0.3s; background: #FAFAFE; color: #2D2B3D; }
36
+ textarea:focus { outline: none; border-color: #7C5CFC; box-shadow: 0 0 0 4px rgba(124,92,252,0.1); background: white; }
37
+ .presets { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px; }
38
+ .preset-btn { padding: 10px 16px; border: 2px solid #E8E6F0; border-radius: 12px; background: white; cursor: pointer; font-size: 13px; font-weight: 600; color: #6B6880; transition: all 0.2s; flex: 1; min-width: 100px; text-align: center; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  .preset-btn:hover { border-color: #7C5CFC; color: #7C5CFC; transform: translateY(-1px); }
40
  .preset-btn.active { border-color: #7C5CFC; background: #7C5CFC; color: white; }
41
+ .toggle-section { background: linear-gradient(135deg, #F8F5FF, #FFF5F0); border-radius: 16px; padding: 24px; margin-bottom: 24px; border: 1px solid rgba(124,92,252,0.1); }
42
+ .toggle-row { display: flex; justify-content: space-between; align-items: center; }
43
+ .toggle-info h4 { font-size: 14px; font-weight: 700; color: #4A4861; }
44
+ .toggle-info p { font-size: 12px; color: #8B87A0; margin-top: 4px; }
45
+ .toggle-switch { position: relative; width: 56px; height: 30px; cursor: pointer; }
46
+ .toggle-switch input { opacity: 0; width: 0; height: 0; }
47
+ .toggle-slider { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #D0CDE0; border-radius: 30px; transition: 0.3s; }
48
+ .toggle-slider:before { content: ""; position: absolute; height: 24px; width: 24px; left: 3px; bottom: 3px; background: white; border-radius: 50%; transition: 0.3s; box-shadow: 0 2px 4px rgba(0,0,0,0.15); }
49
+ input:checked + .toggle-slider { background: linear-gradient(135deg, #7C5CFC, #FF6B35); }
50
+ input:checked + .toggle-slider:before { transform: translateX(26px); }
51
+ .voice-clone-section { background: linear-gradient(135deg, #F0FFF4, #F0F8FF); border-radius: 16px; padding: 24px; margin-bottom: 24px; border: 1px solid rgba(0,201,167,0.15); }
52
+ .voice-clone-section h4 { font-size: 14px; font-weight: 700; color: #00896B; margin-bottom: 12px; }
53
+ .voice-clone-section p { font-size: 12px; color: #6B8F80; margin-bottom: 16px; line-height: 1.6; }
54
+ .upload-area { border: 2px dashed #B0D8C8; border-radius: 14px; padding: 28px; text-align: center; cursor: pointer; transition: all 0.3s; background: rgba(255,255,255,0.5); }
55
+ .upload-area:hover { border-color: #00C9A7; background: rgba(255,255,255,0.8); }
56
+ .upload-area.has-file { border-color: #00C9A7; border-style: solid; background: rgba(0,201,167,0.05); }
57
+ .upload-icon { font-size: 32px; margin-bottom: 8px; }
58
+ .upload-text { font-size: 13px; color: #6B8F80; }
59
+ .upload-text strong { color: #00896B; }
60
+ .file-info { display: none; margin-top: 12px; padding: 12px 16px; background: rgba(0,201,167,0.08); border-radius: 10px; font-size: 13px; color: #00896B; font-weight: 600; }
61
+ .file-info.show { display: flex; align-items: center; justify-content: space-between; }
62
+ .file-remove { background: none; border: none; color: #FF6B35; cursor: pointer; font-size: 13px; font-weight: 700; }
63
+ input[type="file"] { display: none; }
64
+ .gen-btn { width: 100%; padding: 18px; border: none; border-radius: 16px; background: linear-gradient(135deg, #7C5CFC, #9B7AFF); color: white; font-size: 17px; font-weight: 700; cursor: pointer; transition: all 0.3s; font-family: inherit; box-shadow: 0 6px 25px rgba(124,92,252,0.35); }
65
  .gen-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(124,92,252,0.45); }
66
+ .gen-btn:disabled { background: #C5C0D8; cursor: not-allowed; box-shadow: none; transform: none; }
 
 
 
 
67
  .loading { display: none; text-align: center; padding: 40px; }
68
  .loading.show { display: block; }
69
+ .spinner { width: 48px; height: 48px; border: 4px solid #E8E6F0; border-top-color: #7C5CFC; border-radius: 50%; animation: spin 0.8s linear infinite; margin: 0 auto 16px; }
 
 
 
 
70
  @keyframes spin { to { transform: rotate(360deg); } }
71
  .loading-text { font-size: 14px; color: #8B87A0; }
 
72
  .result { display: none; margin-top: 24px; }
73
  .result.show { display: block; }
74
+ .audio-card { background: linear-gradient(135deg, #F0FFF4, #F0F8FF); border: 2px solid #00C9A7; border-radius: 16px; padding: 24px; }
75
+ .audio-card h3 { font-size: 15px; font-weight: 700; color: #00896B; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
 
 
 
 
 
 
76
  audio { width: 100%; margin-top: 8px; border-radius: 8px; }
77
  .meta { font-size: 12px; color: #6B8F80; margin-top: 8px; }
78
+ .error { display: none; color: #E53E3E; background: #FFF5F5; padding: 14px 20px; border-radius: 12px; margin-top: 16px; font-size: 14px; }
79
+ .error.show { display: block; }
80
+ .info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; }
81
+ .info-item { background: rgba(255,255,255,0.7); border-radius: 16px; padding: 20px; text-align: center; border: 1px solid rgba(0,0,0,0.04); }
 
 
 
 
 
82
  .info-item .icon { font-size: 28px; margin-bottom: 8px; }
83
  .info-item .val { font-size: 22px; font-weight: 800; color: #7C5CFC; }
84
  .info-item .lbl { font-size: 11px; color: #8B87A0; text-transform: uppercase; letter-spacing: 0.5px; margin-top: 4px; }
85
+ .arch-box { background: #1E1E2E; color: #CDD6F4; font-family: 'JetBrains Mono', monospace; font-size: 13px; padding: 20px; border-radius: 14px; margin-top: 20px; line-height: 1.8; overflow-x: auto; }
86
+ .arch-box .hl { color: #F9E2AF; font-weight: 600; }
87
+ .arch-box .ok { color: #A6E3A1; }
88
+ .arch-box .dim { color: #6C7086; }
89
+ .footer { text-align: center; padding: 32px; font-size: 13px; color: #8B87A0; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  .footer a { color: #7C5CFC; text-decoration: none; font-weight: 600; }
 
 
 
 
 
 
 
91
  </style>
92
  </head>
93
  <body>
94
+ <div class="bg-orbs"><div class="orb orb-1"></div><div class="orb orb-2"></div><div class="orb orb-3"></div></div>
 
 
 
 
 
 
95
  <div class="container">
 
 
96
  <div class="header">
97
  <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="badge-link">
98
+ <div class="badge">🧬 FINAL-Bench · World's First Cross-Modal FFN Transfer</div>
99
  </a>
100
+ <h1>Darwin-TTS-1.7B-Cross v2</h1>
101
+ <p>Emotion-enhanced TTS via LLM→TTS FFN blending. No training required. Now with <strong>Voice Cloning</strong>!</p>
102
  <div class="header-links">
103
+ <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-model">πŸ€— Model</a>
104
+ <a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank" class="link-blog">πŸ“„ Blog</a>
105
+ <a href="https://huggingface.co/spaces/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-article">πŸš€ Space</a>
106
  </div>
107
  </div>
 
 
108
  <div class="info-grid">
109
+ <div class="info-item"><div class="icon">🧬</div><div class="val">84</div><div class="lbl">FFN Tensors</div></div>
110
+ <div class="info-item"><div class="icon">βœ…</div><div class="val">100%</div><div class="lbl">Shape Match</div></div>
111
+ <div class="info-item"><div class="icon">⚑</div><div class="val">0</div><div class="lbl">Training</div></div>
112
+ <div class="info-item"><div class="icon">πŸŽ™οΈ</div><div class="val">Clone</div><div class="lbl">Voice Cloning</div></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  </div>
 
 
114
  <div class="card">
115
  <div class="input-group">
116
  <label>πŸ’¬ Text to Speak</label>
117
  <textarea id="textInput" placeholder="Enter text in Korean, English, or other supported languages...">μ•ˆλ…•ν•˜μ„Έμš”, μ €λŠ” λ‹€μœˆ 인곡지λŠ₯μž…λ‹ˆλ‹€. λ§Œλ‚˜μ„œ λ°˜κ°‘μŠ΅λ‹ˆλ‹€!</textarea>
118
  </div>
 
119
  <div class="input-group">
120
  <label>πŸ“ Quick Examples</label>
121
  <div class="presets">
 
125
  <button class="preset-btn" onclick="setPreset('였늘 날씨가 정말 μ’‹λ„€μš”. μ‚°μ±…ν•˜κΈ° λ”± 쒋은 λ‚ μ΄μ—μš”.')">🌀️ 일상</button>
126
  </div>
127
  </div>
128
+ <div class="toggle-section">
129
+ <div class="toggle-row">
130
+ <div class="toggle-info">
131
+ <h4>🧬 Darwin Enhancement</h4>
132
+ <p id="toggleDesc">LLM FFN blended β€” more expressive, emotional speech</p>
 
133
  </div>
134
+ <label class="toggle-switch">
135
+ <input type="checkbox" id="darwinToggle" checked onchange="updateToggle()">
136
+ <span class="toggle-slider"></span>
137
+ </label>
 
 
 
 
 
 
 
 
138
  </div>
139
  </div>
140
+ <div class="voice-clone-section">
141
+ <h4>πŸŽ™οΈ Voice Cloning (Optional)</h4>
142
+ <p>Upload 3~15 seconds of clear speech audio. The model will clone this voice.<br>If no audio is uploaded, a default voice will be used.</p>
143
+ <div class="upload-area" id="uploadArea" onclick="document.getElementById('refAudio').click()">
144
+ <div class="upload-icon" id="uploadIcon">🎀</div>
145
+ <div class="upload-text" id="uploadText"><strong>Click to upload</strong> or drag & drop<br>WAV, MP3, FLAC Β· 3~15 seconds recommended</div>
146
+ </div>
147
+ <input type="file" id="refAudio" accept="audio/*" onchange="handleAudioUpload(this)">
148
+ <div class="file-info" id="fileInfo">
149
+ <span id="fileName"></span>
150
+ <button class="file-remove" onclick="removeAudio(event)">βœ• Remove</button>
151
+ </div>
152
  </div>
153
+ <button class="gen-btn" id="genBtn" onclick="generate()">πŸ”Š Generate Speech</button>
154
+ <div class="loading" id="loading"><div class="spinner"></div><div class="loading-text">🧬 Generating speech...<br>This may take 30~60 seconds on first run.</div></div>
155
  <div class="error" id="error"></div>
 
156
  <div class="result" id="result">
157
  <div class="audio-card">
158
  <h3>✨ Darwin-TTS Output</h3>
 
161
  </div>
162
  </div>
163
  </div>
 
 
164
  <div class="card">
165
  <h2 style="font-size:20px;font-weight:800;margin-bottom:20px;color:#2D2B3D;">πŸ”¬ How It Works</h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  <div class="arch-box">
167
+ <span class="dim">Darwin-TTS v2 Architecture:</span><br>
168
+ β”œβ”€β”€ <span class="hl">talker</span> (28L Qwen3 LM) ← <span class="ok">Layer-selective FFN blending (proprietary)</span><br>
169
  β”œβ”€β”€ code_predictor (5L) ← <span class="dim">untouched</span><br>
170
  β”œβ”€β”€ speech_tokenizer ← <span class="dim">untouched</span><br>
171
  └── encoder/decoder ← <span class="dim">untouched</span><br>
172
  <br>
173
+ <span class="dim">v2:</span> <span class="ok">Long text stability + quality exceeds original + Voice Cloning</span>
174
  </div>
175
  </div>
 
 
176
  <div class="footer">
177
+ <p>Built by <a href="https://huggingface.co/FINAL-Bench" target="_blank">VIDRAFT</a> Β· Darwin Evolutionary Merge Framework</p>
178
  <p style="margin-top:6px;">
179
+ <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank">πŸ€— Model</a> Β·
180
+ <a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank">πŸ“„ Blog</a> Β· Apache 2.0
 
 
181
  </p>
182
  </div>
 
183
  </div>
 
184
  <script>
185
+ let refAudioB64 = null;
186
+ function updateToggle() {
187
+ const on = document.getElementById('darwinToggle').checked;
188
+ document.getElementById('toggleDesc').textContent = on ? 'LLM FFN blended β€” more expressive, emotional speech' : 'Original Qwen3-TTS β€” no enhancement';
 
 
 
 
 
 
 
 
189
  }
 
190
  function setPreset(text) {
191
  document.getElementById('textInput').value = text;
192
  document.querySelectorAll('.preset-btn').forEach(b => b.classList.remove('active'));
193
  event.target.classList.add('active');
194
  }
195
+ function handleAudioUpload(input) {
196
+ const file = input.files[0];
197
+ if (!file) return;
198
+ const reader = new FileReader();
199
+ reader.onload = function(e) {
200
+ const bytes = new Uint8Array(e.target.result);
201
+ let binary = '';
202
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
203
+ refAudioB64 = btoa(binary);
204
+ document.getElementById('uploadArea').classList.add('has-file');
205
+ document.getElementById('uploadIcon').textContent = 'βœ…';
206
+ document.getElementById('uploadText').innerHTML = '<strong>' + file.name + '</strong><br>' + (file.size / 1024).toFixed(0) + ' KB';
207
+ document.getElementById('fileName').textContent = '🎀 ' + file.name;
208
+ document.getElementById('fileInfo').classList.add('show');
209
+ };
210
+ reader.readAsArrayBuffer(file);
211
+ }
212
+ function removeAudio(e) {
213
+ e.stopPropagation();
214
+ refAudioB64 = null;
215
+ document.getElementById('refAudio').value = '';
216
+ document.getElementById('uploadArea').classList.remove('has-file');
217
+ document.getElementById('uploadIcon').textContent = '🎀';
218
+ document.getElementById('uploadText').innerHTML = '<strong>Click to upload</strong> or drag & drop<br>WAV, MP3, FLAC Β· 3~15 seconds recommended';
219
+ document.getElementById('fileInfo').classList.remove('show');
220
+ }
221
+ const ua = document.getElementById('uploadArea');
222
+ ua.addEventListener('dragover', e => { e.preventDefault(); ua.style.borderColor = '#00C9A7'; });
223
+ ua.addEventListener('dragleave', () => { ua.style.borderColor = ''; });
224
+ ua.addEventListener('drop', e => { e.preventDefault(); ua.style.borderColor = ''; if (e.dataTransfer.files.length) { document.getElementById('refAudio').files = e.dataTransfer.files; handleAudioUpload(document.getElementById('refAudio')); }});
225
  async function generate() {
226
  const text = document.getElementById('textInput').value.trim();
227
+ const useDarwin = document.getElementById('darwinToggle').checked;
 
228
  if (!text) return;
 
229
  const btn = document.getElementById('genBtn');
230
  const loading = document.getElementById('loading');
231
  const result = document.getElementById('result');
232
  const error = document.getElementById('error');
233
+ btn.disabled = true; btn.textContent = '⏳ Generating...';
234
+ loading.classList.add('show'); result.classList.remove('show'); error.classList.remove('show');
 
 
 
 
 
235
  try {
236
+ const body = { text, use_darwin: useDarwin };
237
+ if (refAudioB64) body.ref_audio = refAudioB64;
238
+ const resp = await fetch('/synthesize', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
239
+ if (!resp.ok) { const err = await resp.json(); throw new Error(err.detail || 'Generation failed'); }
 
 
 
 
 
 
 
240
  const blob = await resp.blob();
241
  const url = URL.createObjectURL(blob);
242
  const dur = resp.headers.get('X-Duration') || '?';
243
+ const model = resp.headers.get('X-Model') || (useDarwin ? 'Darwin' : 'Original');
244
  document.getElementById('audioPlayer').src = url;
245
+ document.getElementById('audioMeta').textContent = 'Duration: ' + dur + 's Β· Model: ' + model + ' Β· Voice: ' + (refAudioB64 ? 'Cloned' : 'Default');
 
246
  result.classList.add('show');
247
+ } catch (e) { error.textContent = '❌ ' + e.message; error.classList.add('show'); }
248
+ finally { btn.disabled = false; btn.textContent = 'πŸ”Š Generate Speech'; loading.classList.remove('show'); }
 
 
 
 
 
 
 
249
  }
250
  </script>
251
  </body>