Interactive voice demo · 2026

Voiced Gnome

Giving an animated character a voice—and keeping the interaction responsive.

voiced-gnome

I adapted the Spine character and wardrobe from Pebbler for Telegram into a standalone voice demo. Each of the 18 characters has its own outfit, skin palette, fictional backstory and stock voice. The browser coordinates conversation state, speech playback, mouth movements and expressive gestures.

Meet the characters

Use the arrows to choose a character. About me plays a recorded introduction with mouth cues and timed emotes; typed and spoken conversations use ElevenLabs. The demo loads on request, and microphone permission is requested only after choosing Speak.

Source code loads when you open this view.

A pirate gnome with Ranger and Dragon characters on either side

Browse 18 characters and play About me. Choose Speak to use your microphone, or type a question. Live replies use ElevenLabs and require a visitor check.

Making speech and motion agree

The face uses nine mouth attachments. Recorded introductions follow cues derived from the actual audio, against the playback clock. Live speech combines provider character alignment, a pronunciation lookup and an amplitude fallback. Driving movement from playback time keeps buffering from advancing the mouth ahead of the voice.

The selected gnome stays large while neighbouring characters preview the next outfits. Wardrobe textures load on demand. Character changes, interruptions and cancelled requests clear the previous speech and gesture state so an old reply cannot take over the newly selected character.

Expressive tools with a small boundary

The agent can request an expression or emote through validated tools. The browser accepts known actions and applies them to the existing rig. Typed replies are split into bounded speech requests; the Worker selects the character voice and speech model from the shared catalog. Shared Zod schemas connect the client, Worker and types package.

Protecting a public voice demo

Paid requests require server-validated Turnstile, a signed visitor capability and an idempotency key. SQLite Durable Objects reserve per-visitor and daily allowances before generation, reject duplicate or overlapping work, and preserve quotas across restarts. Static browsing and introductions remain available when generation is paused or an allowance is exhausted.

Live signed URLs can be reused, so URL admission counts are not voice-minute accounting. The provider enforces call duration, concurrency and daily limits independently. A stricter per-visitor voice-minute allowance would need a server-owned relay. The repository documents that limitation alongside runtime tests for concurrent quota claims and persistence.

Controls, tests and tradeoffs