Voice and text I/O for Svelte 5
Give your interface a voice.
A Svelte 5 controller for ElevenLabs Conversational AI, with a SvelteKit showcase in this repository. It opens the socket, negotiates the audio format, turns protocol events into reactive state, and releases every browser resource when the session ends.
- Svelte 5runes · SSR-safe
- Signed URLskey stays server-side
- 8 to 48 kHzPCM · AudioWorklet
01 Live console
Talk to the machine.
This static build connects with public agent ids. Paste one, press start, and the browser opens the audio line. Signed URLs require the server build.
- First audio
- -
- You / in
- -
- Agent / out
- -
- Turns
- 00
- Barge-ins
- 00
The line is quiet. Press start, grant the microphone, and talk.
Agent text may stream in word by word. Corrections replace the same entry.
02 Under the hood
A short path from
click to conversation.
The controller owns the socket, the audio graph, and the teardown. Your component owns the interface.
- 01
Prepare inside the gesture
Microphone permission and both AudioContexts start in the click handler, avoiding autoplay gates. Then the URL is resolved and the socket opens with the
convaisubprotocol. - 02
Stream up
A worklet captures ~100 ms chunks. They are low-pass filtered, resampled to the negotiated rate, encoded as PCM16 little-endian base64, and sent as
user_audio_chunk. - 03
Schedule down
Agent chunks are decoded onto one playback clock. Two
AnalyserNodeinstances feed the spectra; local or server barge-in flushes every queued source with a 12 ms gain ramp. - 04
Leave nothing running
An
$effectteardown closes the socket, stops media tracks, disconnects the graph, revokes worklet URLs, closes both contexts, and clears its timers.
03 Install and start
Five lines to
a first sentence.
Install the package, hand the controller an agent id, and start a session. Public ids work anywhere. Signed mode adds one server route and keeps the key out of the bundle.
bun add svelte-elevenlabs Peer dependency: Svelte 5. The library is ESM-only and publishes its TypeScript surface. The package tooling runs on Node 22.17+ or Bun 1.4+.
# .env: never expose this through PUBLIC_*
ELEVENLABS_API_KEY=your_key_here
# optional demo prefill
PUBLIC_ELEVENLABS_AGENT_ID=agent_examplePublicId The included SvelteKit route reads process.env and returns only a short-lived
signed URL.
bun install
bun run dev Open the console on localhost or HTTPS. Microphone access is blocked in insecure
contexts by design.
<script lang="ts">
import { useConversation } from 'svelte-elevenlabs';
const conversation = useConversation({ agentId: 'agent_…' });
</script>
<button onclick={() => conversation.startConversation()}>Talk</button> Controller fields use Svelte 5 runes: read conversation.status, transcripts, and volume directly in markup.
When using ConversationWidget in your own Tailwind app, add @source '../node_modules/svelte-elevenlabs/dist'; to your stylesheet so utility
classes can be discovered. Scoped component styles do not require it.
Using Claude Code, Cursor, OpenCode, Copilot, Windsurf, Cline, or Gemini CLI? Run bunx svelte-elevenlabs agents install to add one reviewed guide to the file
your tool already reads, then see the agent kit docs.
04 API map
Small surface.
Deep control.
The core controller exposes the reactive values and lifecycle methods the interface needs, including text only sessions, client tools, feedback, and device control. Everything else is an optional extension.
status- "disconnected" | "connecting" | "connected"
isSpeaking- Agent playback is scheduled or audible.
isListening- Microphone is live and the agent can hear it.
isTextOnly- The current or next session is text only.
volume- number: combined level, 0 to 1.
transcripts- TranscriptMessage[]: stable streaming entries.
error- string | null: safe to render; code in
errorCode.
startConversation(target?, { signed?, textOnly? }) Opens the mic or text channel and the socket. Accepts a public agent id, signed wss:// URL, or configured default. textOnly overrides the controller
default for this session.
stopConversation() Ends the call, flushes playback, closes audio, and preserves transcripts.
toggleMute() Stops outgoing chunks without closing the microphone track. Instant undo.
sendContextualUpdate(text) Injects context for the agent's next turn without interrupting speech.
sendUserMessage(text) Adds a typed user turn over the same WebSocket.
sendFeedback(like, eventId?) Submits positive or negative feedback for an agent response.
changeInputDevice(id?) Switches the microphone while a voice session is active.
changeOutputDevice(id?) Routes agent playback to another output device.
Use the parts, or none of them.
<AudioVisualizer> renders the live analyser data. <ConversationWidget> packages the field controls, transcript, mute, and
telemetry. Both accept a controller you already own.
import { AudioVisualizer, ConversationWidget } from 'svelte-elevenlabs';Component lifecycle. Call useConversation during component
initialisation; it registers the cleanup effect. Use createConversation when you need to own teardown yourself.
05 Community file
Built against
the public protocol.
This is an independent library, not an official SDK. It follows the Conversational AI WebSocket protocol, documents its failure modes, and prefers a clear error over a silent dead line.
This is an independent community project. Not affiliated with or endorsed by ElevenLabs Inc.