100% in-browser, hands-free AI voice chat using Whisper, WebLLM, and Supertonic TTS
194
stars
14
commits
TypeScript
primary language
Dec 11, 2025
updated
A hands-free AI voice assistant that runs entirely in your browser. Speech recognition, LLM, and text-to-speech all run locally using WebGPU - no API keys, no server, no data leaves your device. Just talk naturally and the AI responds.
Try it now: HuggingFace Space
Everything runs in your browser:
No audio leaves your device. No API keys needed. Just open and talk.
The built-in LLM is just a demo. The real value is the voice pipeline - STT, VAD, and TTS all wired up and working. Rip out the tiny in-browser model and point it at any LLM you want:
It's ~10 lines of code to swap. See Using a Different LLM below.
# Install dependencies
pnpm install
# Run development server
pnpm dev
Open http://localhost:3000 in Chrome or Edge.
| Asset | Size | When | Cached |
|---|---|---|---|
| Voice embeddings | ~500KB | Included in repo | ✓ Already local |
| Whisper STT model | ~150MB | First use | ✓ IndexedDB |
| Silero VAD model | ~2MB | First use | ✓ IndexedDB |
| Qwen 1.5B LLM | ~900MB | First use | ✓ IndexedDB |
| Supertonic TTS | ~50MB | First use | ✓ IndexedDB |
First load downloads ~1GB of models from HuggingFace CDN. After that, everything runs offline.
Falls back to WASM if WebGPU unavailable (slower but works everywhere).
┌─────────────────────────────────────────────────────────────┐
│ Browser │
│ │
│ Microphone │
│ | │
│ v │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Silero │ > │ Whisper │ > │ WebLLM │ > │Supertonic│ │
│ │ VAD │ │ STT │ │ (Qwen) │ │ TTS │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ | | | | │
│ Detects Transcribes Generates Speaks │
│ speech to text response response │
│ │
└─────────────────────────────────────────────────────────────┘
src/
├── app/
│ ├── page.tsx # Main voice chat UI
│ ├── layout.tsx # App layout
│ └── globals.css # Styles
├── components/ui/ # UI components
├── hooks/
│ ├── use-webllm.ts # WebLLM integration
│ └── use-tts.ts # TTS integration
└── lib/
├── tts.ts # TTS pipeline
└── splitter.ts # Text chunking
public/
├── stt-worker-esm.js # Whisper + VAD worker
├── vad-processor.js # Audio worklet
└── voices/ # TTS voice embeddings (bundled)
This demo uses WebLLM for fully local operation. To use an external LLM instead:
src/app/api/chat/route.ts)page.tsx, find handleLLMResponse() and replace the WebLLM call:// Instead of webllm.chat(), call your API:
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ messages: conversationHistory })
});
const data = await response.json();
return data.response;
10 voices bundled (5 female, 5 male):
MIT License - see LICENSE
14 commits
TypeScript
84.7%
JavaScript
11.0%
CSS
4.3%
100% in-browser, hands-free AI voice chat using Whisper, WebLLM, and Supertonic TTS
194
stars
14
commits
TypeScript
primary language
Dec 11, 2025
updated
A hands-free AI voice assistant that runs entirely in your browser. Speech recognition, LLM, and text-to-speech all run locally using WebGPU - no API keys, no server, no data leaves your device. Just talk naturally and the AI responds.
Try it now: HuggingFace Space
Everything runs in your browser:
No audio leaves your device. No API keys needed. Just open and talk.
The built-in LLM is just a demo. The real value is the voice pipeline - STT, VAD, and TTS all wired up and working. Rip out the tiny in-browser model and point it at any LLM you want:
It's ~10 lines of code to swap. See Using a Different LLM below.
# Install dependencies
pnpm install
# Run development server
pnpm dev
Open http://localhost:3000 in Chrome or Edge.
| Asset | Size | When | Cached |
|---|---|---|---|
| Voice embeddings | ~500KB | Included in repo | ✓ Already local |
| Whisper STT model | ~150MB | First use | ✓ IndexedDB |
| Silero VAD model | ~2MB | First use | ✓ IndexedDB |
| Qwen 1.5B LLM | ~900MB | First use | ✓ IndexedDB |
| Supertonic TTS | ~50MB | First use | ✓ IndexedDB |
First load downloads ~1GB of models from HuggingFace CDN. After that, everything runs offline.
Falls back to WASM if WebGPU unavailable (slower but works everywhere).
┌─────────────────────────────────────────────────────────────┐
│ Browser │
│ │
│ Microphone │
│ | │
│ v │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Silero │ > │ Whisper │ > │ WebLLM │ > │Supertonic│ │
│ │ VAD │ │ STT │ │ (Qwen) │ │ TTS │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ | | | | │
│ Detects Transcribes Generates Speaks │
│ speech to text response response │
│ │
└─────────────────────────────────────────────────────────────┘
src/
├── app/
│ ├── page.tsx # Main voice chat UI
│ ├── layout.tsx # App layout
│ └── globals.css # Styles
├── components/ui/ # UI components
├── hooks/
│ ├── use-webllm.ts # WebLLM integration
│ └── use-tts.ts # TTS integration
└── lib/
├── tts.ts # TTS pipeline
└── splitter.ts # Text chunking
public/
├── stt-worker-esm.js # Whisper + VAD worker
├── vad-processor.js # Audio worklet
└── voices/ # TTS voice embeddings (bundled)
This demo uses WebLLM for fully local operation. To use an external LLM instead:
src/app/api/chat/route.ts)page.tsx, find handleLLMResponse() and replace the WebLLM call:// Instead of webllm.chat(), call your API:
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ messages: conversationHistory })
});
const data = await response.json();
return data.response;
10 voices bundled (5 female, 5 male):
MIT License - see LICENSE
14 commits
TypeScript
84.7%
JavaScript
11.0%
CSS
4.3%