| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>𧬠Darwin-TTS-1.7B-Cross</title> |
| <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"> |
| <style> |
| * { margin: 0; padding: 0; box-sizing: border-box; } |
| 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; } |
| .bg-orbs { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; } |
| .orb { position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0.15; animation: float 20s ease-in-out infinite; } |
| .orb-1 { width: 400px; height: 400px; background: #FF6B35; top: -100px; right: -100px; } |
| .orb-2 { width: 350px; height: 350px; background: #7C5CFC; bottom: -50px; left: -50px; animation-delay: -7s; } |
| .orb-3 { width: 300px; height: 300px; background: #00C9A7; top: 50%; left: 40%; animation-delay: -14s; } |
| @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); } } |
| .container { max-width: 800px; margin: 0 auto; padding: 32px 20px; position: relative; z-index: 1; } |
| .header { text-align: center; margin-bottom: 36px; } |
| .badge-link { text-decoration: none; } |
| .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; } |
| .badge:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255,107,53,0.4); } |
| .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; } |
| .header p { font-size: 16px; color: #6B6880; max-width: 600px; margin: 0 auto; line-height: 1.6; } |
| .header-links { display: flex; justify-content: center; gap: 12px; margin-top: 16px; flex-wrap: wrap; } |
| .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; } |
| .link-model { background: #7C5CFC; color: white; box-shadow: 0 3px 12px rgba(124,92,252,0.3); } |
| .link-model:hover { transform: translateY(-1px); box-shadow: 0 5px 18px rgba(124,92,252,0.4); } |
| .link-blog { background: white; color: #7C5CFC; border: 2px solid #E8E6F0; } |
| .link-blog:hover { border-color: #7C5CFC; transform: translateY(-1px); } |
| .link-article { background: white; color: #FF6B35; border: 2px solid #FFE0D0; } |
| .link-article:hover { border-color: #FF6B35; transform: translateY(-1px); } |
| .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; } |
| .input-group { margin-bottom: 24px; } |
| .input-group label { display: block; font-size: 13px; font-weight: 700; color: #4A4861; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; } |
| 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; } |
| textarea:focus { outline: none; border-color: #7C5CFC; box-shadow: 0 0 0 4px rgba(124,92,252,0.1); background: white; } |
| .presets { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px; } |
| .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; } |
| .preset-btn:hover { border-color: #7C5CFC; color: #7C5CFC; transform: translateY(-1px); } |
| .preset-btn.active { border-color: #7C5CFC; background: #7C5CFC; color: white; } |
| .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); } |
| .toggle-row { display: flex; justify-content: space-between; align-items: center; } |
| .toggle-info h4 { font-size: 14px; font-weight: 700; color: #4A4861; } |
| .toggle-info p { font-size: 12px; color: #8B87A0; margin-top: 4px; } |
| .toggle-switch { position: relative; width: 56px; height: 30px; cursor: pointer; } |
| .toggle-switch input { opacity: 0; width: 0; height: 0; } |
| .toggle-slider { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #D0CDE0; border-radius: 30px; transition: 0.3s; } |
| .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); } |
| input:checked + .toggle-slider { background: linear-gradient(135deg, #7C5CFC, #FF6B35); } |
| input:checked + .toggle-slider:before { transform: translateX(26px); } |
| .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); } |
| .voice-clone-section h4 { font-size: 14px; font-weight: 700; color: #00896B; margin-bottom: 12px; } |
| .voice-clone-section p { font-size: 12px; color: #6B8F80; margin-bottom: 16px; line-height: 1.6; } |
| .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); } |
| .upload-area:hover { border-color: #00C9A7; background: rgba(255,255,255,0.8); } |
| .upload-area.has-file { border-color: #00C9A7; border-style: solid; background: rgba(0,201,167,0.05); } |
| .upload-icon { font-size: 32px; margin-bottom: 8px; } |
| .upload-text { font-size: 13px; color: #6B8F80; } |
| .upload-text strong { color: #00896B; } |
| .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; } |
| .file-info.show { display: flex; align-items: center; justify-content: space-between; } |
| .file-remove { background: none; border: none; color: #FF6B35; cursor: pointer; font-size: 13px; font-weight: 700; } |
| input[type="file"] { display: none; } |
| .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); } |
| .gen-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(124,92,252,0.45); } |
| .gen-btn:disabled { background: #C5C0D8; cursor: not-allowed; box-shadow: none; transform: none; } |
| .loading { display: none; text-align: center; padding: 40px; } |
| .loading.show { display: block; } |
| .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; } |
| @keyframes spin { to { transform: rotate(360deg); } } |
| .loading-text { font-size: 14px; color: #8B87A0; } |
| .result { display: none; margin-top: 24px; } |
| .result.show { display: block; } |
| .audio-card { background: linear-gradient(135deg, #F0FFF4, #F0F8FF); border: 2px solid #00C9A7; border-radius: 16px; padding: 24px; } |
| .audio-card h3 { font-size: 15px; font-weight: 700; color: #00896B; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; } |
| audio { width: 100%; margin-top: 8px; border-radius: 8px; } |
| .meta { font-size: 12px; color: #6B8F80; margin-top: 8px; } |
| .error { display: none; color: #E53E3E; background: #FFF5F5; padding: 14px 20px; border-radius: 12px; margin-top: 16px; font-size: 14px; } |
| .error.show { display: block; } |
| .info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; } |
| .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); } |
| .info-item .icon { font-size: 28px; margin-bottom: 8px; } |
| .info-item .val { font-size: 22px; font-weight: 800; color: #7C5CFC; } |
| .info-item .lbl { font-size: 11px; color: #8B87A0; text-transform: uppercase; letter-spacing: 0.5px; margin-top: 4px; } |
| .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; } |
| .arch-box .hl { color: #F9E2AF; font-weight: 600; } |
| .arch-box .ok { color: #A6E3A1; } |
| .arch-box .dim { color: #6C7086; } |
| .footer { text-align: center; padding: 32px; font-size: 13px; color: #8B87A0; } |
| .footer a { color: #7C5CFC; text-decoration: none; font-weight: 600; } |
| </style> |
| </head> |
| <body> |
| <div class="bg-orbs"><div class="orb orb-1"></div><div class="orb orb-2"></div><div class="orb orb-3"></div></div> |
| <div class="container"> |
| <div class="header"> |
| <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="badge-link"> |
| <div class="badge">𧬠FINAL-Bench · World's First Cross-Modal FFN Transfer</div> |
| </a> |
| <h1>Darwin-TTS-1.7B-Cross v2</h1> |
| <p>Emotion-enhanced TTS via LLMβTTS FFN blending. No training required. Now with <strong>Voice Cloning</strong>!</p> |
| <div class="header-links"> |
| <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-model">π€ Model</a> |
| <a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank" class="link-blog">π Blog</a> |
| <a href="https://huggingface.co/spaces/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-article">π Space</a> |
| </div> |
| </div> |
| <div class="info-grid"> |
| <div class="info-item"><div class="icon">π§¬</div><div class="val">84</div><div class="lbl">FFN Tensors</div></div> |
| <div class="info-item"><div class="icon">β
</div><div class="val">100%</div><div class="lbl">Shape Match</div></div> |
| <div class="info-item"><div class="icon">β‘</div><div class="val">0</div><div class="lbl">Training</div></div> |
| <div class="info-item"><div class="icon">ποΈ</div><div class="val">Clone</div><div class="lbl">Voice Cloning</div></div> |
| </div> |
| <div class="card"> |
| <div class="input-group"> |
| <label>π¬ Text to Speak</label> |
| <textarea id="textInput" placeholder="Enter text in Korean, English, or other supported languages...">μλ
νμΈμ, μ λ λ€μ μΈκ³΅μ§λ₯μ
λλ€. λ§λμ λ°κ°μ΅λλ€!</textarea> |
| </div> |
| <div class="input-group"> |
| <label>π Quick Examples</label> |
| <div class="presets"> |
| <button class="preset-btn" onclick="setPreset('μλ
νμΈμ, μ λ λ€μ μΈκ³΅μ§λ₯μ
λλ€.')">π°π· μΈμ¬</button> |
| <button class="preset-btn" onclick="setPreset('μ λ§ κΈ°μ μμμ΄μμ! λλμ΄ ν΄λμ΅λλ€!')">π κΈ°μ¨</button> |
| <button class="preset-btn" onclick="setPreset('Hello, I am Darwin AI. Nice to meet you!')">πΊπΈ English</button> |
| <button class="preset-btn" onclick="setPreset('μ€λ λ μ¨κ° μ λ§ μ’λ€μ. μ°μ±
νκΈ° λ± μ’μ λ μ΄μμ.')">π€οΈ μΌμ</button> |
| </div> |
| </div> |
| <div class="toggle-section"> |
| <div class="toggle-row"> |
| <div class="toggle-info"> |
| <h4>𧬠Darwin Enhancement</h4> |
| <p id="toggleDesc">LLM FFN blended β more expressive, emotional speech</p> |
| </div> |
| <label class="toggle-switch"> |
| <input type="checkbox" id="darwinToggle" checked onchange="updateToggle()"> |
| <span class="toggle-slider"></span> |
| </label> |
| </div> |
| </div> |
| <div class="voice-clone-section"> |
| <h4>ποΈ Voice Cloning (Optional)</h4> |
| <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> |
| <div class="upload-area" id="uploadArea" onclick="document.getElementById('refAudio').click()"> |
| <div class="upload-icon" id="uploadIcon">π€</div> |
| <div class="upload-text" id="uploadText"><strong>Click to upload</strong> or drag & drop<br>WAV, MP3, FLAC Β· 3~15 seconds recommended</div> |
| </div> |
| <input type="file" id="refAudio" accept="audio/*" onchange="handleAudioUpload(this)"> |
| <div class="file-info" id="fileInfo"> |
| <span id="fileName"></span> |
| <button class="file-remove" onclick="removeAudio(event)">β Remove</button> |
| </div> |
| </div> |
| <button class="gen-btn" id="genBtn" onclick="generate()">π Generate Speech</button> |
| <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> |
| <div class="error" id="error"></div> |
| <div class="result" id="result"> |
| <div class="audio-card"> |
| <h3>β¨ Darwin-TTS Output</h3> |
| <audio id="audioPlayer" controls></audio> |
| <div class="meta" id="audioMeta"></div> |
| </div> |
| </div> |
| </div> |
| <div class="card"> |
| <h2 style="font-size:20px;font-weight:800;margin-bottom:20px;color:#2D2B3D;">π¬ How It Works</h2> |
| <div class="arch-box"> |
| <span class="dim">Darwin-TTS v2 Architecture:</span><br> |
| βββ <span class="hl">talker</span> (28L Qwen3 LM) β <span class="ok">Layer-selective FFN blending (proprietary)</span><br> |
| βββ code_predictor (5L) β <span class="dim">untouched</span><br> |
| βββ speech_tokenizer β <span class="dim">untouched</span><br> |
| βββ encoder/decoder β <span class="dim">untouched</span><br> |
| <br> |
| <span class="dim">v2:</span> <span class="ok">Long text stability + quality exceeds original + Voice Cloning</span> |
| </div> |
| </div> |
| <div class="footer"> |
| <p>Built by <a href="https://huggingface.co/FINAL-Bench" target="_blank">VIDRAFT</a> Β· Darwin Evolutionary Merge Framework</p> |
| <p style="margin-top:6px;"> |
| <a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank">π€ Model</a> Β· |
| <a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank">π Blog</a> Β· Apache 2.0 |
| </p> |
| </div> |
| </div> |
| <script> |
| let refAudioB64 = null; |
| function updateToggle() { |
| const on = document.getElementById('darwinToggle').checked; |
| document.getElementById('toggleDesc').textContent = on ? 'LLM FFN blended β more expressive, emotional speech' : 'Original Qwen3-TTS β no enhancement'; |
| } |
| function setPreset(text) { |
| document.getElementById('textInput').value = text; |
| document.querySelectorAll('.preset-btn').forEach(b => b.classList.remove('active')); |
| event.target.classList.add('active'); |
| } |
| function handleAudioUpload(input) { |
| const file = input.files[0]; |
| if (!file) return; |
| const reader = new FileReader(); |
| reader.onload = function(e) { |
| const bytes = new Uint8Array(e.target.result); |
| let binary = ''; |
| for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]); |
| refAudioB64 = btoa(binary); |
| document.getElementById('uploadArea').classList.add('has-file'); |
| document.getElementById('uploadIcon').textContent = 'β
'; |
| document.getElementById('uploadText').innerHTML = '<strong>' + file.name + '</strong><br>' + (file.size / 1024).toFixed(0) + ' KB'; |
| document.getElementById('fileName').textContent = 'π€ ' + file.name; |
| document.getElementById('fileInfo').classList.add('show'); |
| }; |
| reader.readAsArrayBuffer(file); |
| } |
| function removeAudio(e) { |
| e.stopPropagation(); |
| refAudioB64 = null; |
| document.getElementById('refAudio').value = ''; |
| document.getElementById('uploadArea').classList.remove('has-file'); |
| document.getElementById('uploadIcon').textContent = 'π€'; |
| document.getElementById('uploadText').innerHTML = '<strong>Click to upload</strong> or drag & drop<br>WAV, MP3, FLAC Β· 3~15 seconds recommended'; |
| document.getElementById('fileInfo').classList.remove('show'); |
| } |
| const ua = document.getElementById('uploadArea'); |
| ua.addEventListener('dragover', e => { e.preventDefault(); ua.style.borderColor = '#00C9A7'; }); |
| ua.addEventListener('dragleave', () => { ua.style.borderColor = ''; }); |
| 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')); }}); |
| async function generate() { |
| const text = document.getElementById('textInput').value.trim(); |
| const useDarwin = document.getElementById('darwinToggle').checked; |
| if (!text) return; |
| const btn = document.getElementById('genBtn'); |
| const loading = document.getElementById('loading'); |
| const result = document.getElementById('result'); |
| const error = document.getElementById('error'); |
| btn.disabled = true; btn.textContent = 'β³ Generating...'; |
| loading.classList.add('show'); result.classList.remove('show'); error.classList.remove('show'); |
| try { |
| const body = { text, use_darwin: useDarwin }; |
| if (refAudioB64) body.ref_audio = refAudioB64; |
| const resp = await fetch('/synthesize', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); |
| if (!resp.ok) { const err = await resp.json(); throw new Error(err.detail || 'Generation failed'); } |
| const blob = await resp.blob(); |
| const url = URL.createObjectURL(blob); |
| const dur = resp.headers.get('X-Duration') || '?'; |
| const model = resp.headers.get('X-Model') || (useDarwin ? 'Darwin' : 'Original'); |
| document.getElementById('audioPlayer').src = url; |
| document.getElementById('audioMeta').textContent = 'Duration: ' + dur + 's Β· Model: ' + model + ' Β· Voice: ' + (refAudioB64 ? 'Cloned' : 'Default'); |
| result.classList.add('show'); |
| } catch (e) { error.textContent = 'β ' + e.message; error.classList.add('show'); } |
| finally { btn.disabled = false; btn.textContent = 'π Generate Speech'; loading.classList.remove('show'); } |
| } |
| </script> |
| </body> |
| </html> |