// Web Worker for Kokoro TTS — runs ONNX neural network inference off the main thread. // This prevents the 1-5 second freezes that occur when generate() runs on the UI thread. import type { KokoroTTS } from 'kokoro-js' let tts: KokoroTTS | null = null self.addEventListener('message', async (e: MessageEvent) => { const msg = e.data switch (msg.type) { case 'init': { try { const { KokoroTTS } = await import('kokoro-js') tts = await KokoroTTS.from_pretrained('onnx-community/Kokoro-82M-ONNX', { dtype: 'q8', device: null, progress_callback: (p) => { if ('progress' in p && typeof p.progress === 'number') { self.postMessage({ type: 'progress', progress: p.progress }) } }, }) self.postMessage({ type: 'init-done' }) } catch (err) { self.postMessage({ type: 'init-failed', error: String(err) }) } break } case 'generate': { if (!tts) { self.postMessage({ type: 'generate-failed', id: msg.id, error: 'not initialized' }) break } try { const result = await tts.generate(msg.text, { voice: msg.voice, speed: msg.speed, }) // Copy to a standalone ArrayBuffer so we can transfer ownership (zero-copy to main thread) const audio = new Float32Array(result.audio) self.postMessage( { type: 'generate-done', id: msg.id, audio, sampleRate: result.sampling_rate }, { transfer: [audio.buffer] }, ) } catch (err) { self.postMessage({ type: 'generate-failed', id: msg.id, error: String(err) }) } break } } })