Spaces:
Running
Running
File size: 10,917 Bytes
6fbae64 294ea8d 6fbae64 016b5f9 6fbae64 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | <!DOCTYPE html>
<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>
|