Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Orbit E-Studio — Private Voice Studio in Your Browser</title> | |
| <link rel="stylesheet" href="style.css"> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500;600;700&family=Nunito:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <!-- Runtime Web (loaded by worker, kept here for potential main thread usage) --> | |
| <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.0/dist/ort.min.js"></script> | |
| </head> | |
| <body> | |
| <!-- Ambient Background Effects --> | |
| <div class="ambient-layer"> | |
| <div class="orb orb--primary"></div> | |
| <div class="orb orb--secondary"></div> | |
| <div class="orb orb--tertiary"></div> | |
| <div class="grid-overlay"></div> | |
| </div> | |
| <div class="app-shell"> | |
| <!-- Hero Header --> | |
| <header class="hero"> | |
| <div class="hero__brand"> | |
| <div class="logo"> | |
| <svg class="logo__icon" viewBox="0 0 32 32" fill="none"> | |
| <path d="M16 4C16 4 8 8 8 16C8 24 16 28 16 28" stroke="url(#logoGrad)" stroke-width="2.5" stroke-linecap="round" /> | |
| <path d="M16 4C16 4 24 8 24 16C24 24 16 28 16 28" stroke="url(#logoGrad)" stroke-width="2.5" stroke-linecap="round" /> | |
| <path d="M12 10V22" stroke="url(#logoGrad)" stroke-width="2.5" stroke-linecap="round" /> | |
| <path d="M16 8V24" stroke="url(#logoGrad)" stroke-width="2.5" stroke-linecap="round" /> | |
| <path d="M20 10V22" stroke="url(#logoGrad)" stroke-width="2.5" stroke-linecap="round" /> | |
| <defs> | |
| <linearGradient id="logoGrad" x1="8" y1="4" x2="24" y2="28" gradientUnits="userSpaceOnUse"> | |
| <stop stop-color="#3eb489" /> | |
| <stop offset="0.5" stop-color="#00d4aa" /> | |
| <stop offset="1" stop-color="#7fffd4" /> | |
| </linearGradient> | |
| </defs> | |
| </svg> | |
| <span class="logo__text">Orbit E-Studio</span> | |
| </div> | |
| <div class="hero__badge"> | |
| <span class="badge">Orbit Engine</span> | |
| </div> | |
| </div> | |
| <p class="hero__tagline">Real-time text-to-speech with personal voice presets — running entirely in your browser</p> | |
| </header> | |
| <main class="main"> | |
| <!-- Input Section --> | |
| <section class="input-section"> | |
| <!-- Voice Selection --> | |
| <div class="voice-section"> | |
| <div class="voice-selector"> | |
| <label for="voice-select" class="voice-selector__label">Voice</label> | |
| <select id="voice-select" class="voice-selector__dropdown"> | |
| <option value="">Loading voices...</option> | |
| </select> | |
| </div> | |
| <div class="voice-upload"> | |
| <button id="voice-upload-btn" class="btn btn--outline btn--small"> | |
| <svg class="btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> | |
| <polyline points="17 8 12 3 7 8" /> | |
| <line x1="12" y1="3" x2="12" y2="15" /> | |
| </svg> | |
| <span>Upload Voice</span> | |
| </button> | |
| <input type="file" id="voice-upload" accept="audio/*" hidden> | |
| <span id="voice-upload-status" class="voice-upload-status"></span> | |
| </div> | |
| </div> | |
| <div class="textarea-wrap"> | |
| <textarea id="text-input" placeholder="Type or paste text to generate audio..." aria-label="Text to synthesize"></textarea> | |
| <div class="textarea-meta"> | |
| <span class="char-count"><span id="char-count">0</span> chars</span> | |
| </div> | |
| </div> | |
| <!-- Sample Texts --> | |
| <div class="sample-texts"> | |
| <span class="sample-texts__label">Try:</span> | |
| <button class="sample-btn" data-text="Hello, welcome to Orbit E-Studio. This is a demonstration of real-time speech generation running entirely in your browser.">Demo greeting</button> | |
| <button class="sample-btn" data-text="I completely understand how frustrating this must be for you. Let me take care of this right away and make sure we get it resolved.">Empathetic support</button> | |
| <button class="sample-btn" data-text="Wow, congratulations! That's absolutely fantastic news! I'm so thrilled for you!">Excited</button> | |
| <button class="sample-btn" data-text="I'm really sorry to hear about your loss. Please know that we're here for you, and take all the time you need.">Compassionate</button> | |
| <button class="sample-btn" data-text="Good question. Let’s go step by step—start with the basics, then build from there.">Helpful guide</button> | |
| </div> | |
| <div class="controls"> | |
| <button id="generate-btn" class="btn btn--primary"> | |
| <svg class="btn__icon" viewBox="0 0 24 24" fill="currentColor"> | |
| <polygon points="5,3 19,12 5,21" /> | |
| </svg> | |
| <span class="btn__text">Generate Audio</span> | |
| <div class="btn__loader" id="btn-loader"></div> | |
| </button> | |
| <button id="stop-btn" class="btn btn--secondary" disabled> | |
| <svg class="btn__icon" viewBox="0 0 24 24" fill="currentColor"> | |
| <rect x="6" y="6" width="12" height="12" rx="1" /> | |
| </svg> | |
| <span class="btn__text">Stop</span> | |
| </button> | |
| </div> | |
| </section> | |
| <!-- Output Section: Visualizer + Metrics --> | |
| <section class="output-section"> | |
| <div class="visualizer-panel"> | |
| <div class="visualizer-panel__header"> | |
| <span class="visualizer-panel__title">Audio Output</span> | |
| <div class="status-indicator" id="status-indicator"> | |
| <span class="status-dot"></span> | |
| <span class="status-text" id="stat-status">Idle</span> | |
| </div> | |
| </div> | |
| <div class="visualizer-container"> | |
| <canvas id="visualizer-waveform"></canvas> | |
| <canvas id="visualizer-bars" class="visualizer-bars"></canvas> | |
| </div> | |
| </div> | |
| <div class="metrics-panel"> | |
| <h3 class="metrics-panel__title">Performance</h3> | |
| <div class="metric"> | |
| <div class="metric__header"> | |
| <span class="metric__label">Time to First Byte</span> | |
| <button class="metric__info" aria-label="TTFB explanation" data-tooltip="Time from request until first audio chunk is received"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <circle cx="12" cy="12" r="10" /> | |
| <path d="M12 16v-4M12 8h.01" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="metric__value"> | |
| <span class="metric__number" id="stat-ttfb">--</span> | |
| <span class="metric__unit">ms</span> | |
| </div> | |
| <div class="metric__bar"> | |
| <div class="metric__bar-fill" id="ttfb-bar"></div> | |
| </div> | |
| </div> | |
| <div class="metric metric--highlight"> | |
| <div class="metric__header"> | |
| <span class="metric__label">Real-Time Factor</span> | |
| <button class="metric__info" aria-label="RTFx explanation" data-tooltip="Audio duration divided by processing time. Values above 1x mean faster than real-time playback."> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <circle cx="12" cy="12" r="10" /> | |
| <path d="M12 16v-4M12 8h.01" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="metric__value"> | |
| <span class="metric__number metric__number--large" id="stat-rtfx">--</span> | |
| <span class="metric__unit">x</span> | |
| </div> | |
| <div class="metric__context" id="rtfx-context">>1x = faster than real-time</div> | |
| <div class="metric__note" id="edge-opt-note" style="display: none;">(edge optimization applied)</div> | |
| <div class="metric__note" id="full-gen-note" style="display: none;">(waiting for full generation first)</div> | |
| </div> | |
| <div class="metric metric--status"> | |
| <span class="metric__label">Engine</span> | |
| <div class="model-status" id="model-status"> | |
| <span class="model-status__dot"></span> | |
| <span class="model-status__text">Not ready</span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <footer class="footer"> | |
| <p> | |
| <a href="#" rel="nofollow noopener" onclick="event.preventDefault(); return false;">Orbit E-Studio</a> | |
| <span> — Private Voice Studio</span> | |
| </p> | |
| <p class="footer__disclaimer">Do not use without consent, or for fraud, misinformation, or any harmful/illegal purpose.</p> | |
| </footer> | |
| </div> | |
| <script type="module" src="onnx-streaming.js?v=1"></script> | |
| </body> | |
| </html> | |