Showcase

VoiceRun Showcase · 2026

Voice Chess

Chess, played by voice — say “knight to F3” and it moves. Eight AI opponents. Zero LLMs. Built with VoiceRun in a single Claude Code session.

See it in action

Watch the demo

8
AI opponents, ELO 400–2700
0
LLM calls per game
5
Three.js board worlds
1
Claude Code session to build

How it was built

Claude Code + Fable 5, start to finish

The entire application — handler logic, chess engine, move parser, web UI, Three.js environments, deployment config — was designed and built in a single Claude Code session using Fable 5.

A multi-agent code review caught 10 bugs before the first test. Real game transcripts drove iterative fixes to the move parser. Design passes added Three.js themes, Lichess SVG pieces, and a PWA shell — all in conversation.

claude — chess-voice

The VoiceRun stack

Event-driven Python. Push and deploy.

Step 1 of 4

User speaks

The player says "pawn to E4" into their microphone or clicks a square on the board.

A radical decision

Zero LLM calls. Entirely deterministic.

Move parsing — regex normalization, token matching against lookup tables, then filtering against python-chess legal moves. Handles “pawn to E4”, “knight F3”, “castle kingside”, and dozens of STT mishearings like “Ponda” → “pawn to”.

AI opponent — pure-Python minimax with alpha-beta pruning and piece-square tables. Depth and randomness parameters control difficulty. No API call, no latency, no cost per move.

All speech is templates — each character has canned move lines, reactions, and personality. The handler picks one and slots in the move description.

Why it matters: LLMs add latency, cost, and unpredictability. For structured interactions — games, IVRs, workflows — deterministic logic is faster, cheaper, and more reliable. VoiceRun doesn’t assume you need an LLM.

Production learnings

Promptable STT changed everything

We tried three STT providers on real chess speech. Context-aware prompting was the breakthrough — telling the model what domain to expect dramatically improved accuracy.

Cartesia"Pawn to E4""Pond to eat for"
Deepgram"Pawn to E4""Ponda E4"
GPT-mini"Pawn to E4""Pawn to E4." ✓

The hallucination trap

During menu silence, GPT-4o-mini-transcribe hallucinated from the prompt — generating text like “I see that you’re playing a voice-controlled chess game” as if someone had spoken it.

Three-part fix: semantic VAD (context-aware silence detection), a shorter prompt, and a hallucination filter that silently drops text over 80 characters or containing known generated phrases.

hallucination filter
def _is_hallucination(text):
    if len(text) > 80: return True
    return any(m in text.lower()
        for m in MARKERS)

Bringing characters to life

8 opponents. 8 voices. One engine.

Each character is a unique combination of Inworld TTS voice, personality-driven dialogue, and a configurable engine. No LLM personas — just structured data that makes each opponent feel genuinely different.

Distinct voices, real personality

Every opponent speaks with a different Inworld voice — Josh for the bumbling beginner, Ashley for the intense grandmaster, Ryan for the cold machine. Their lines are canned but characterful: Woody says “Eeny meeny miny moe...” while Kasparov says “Obviously.”

Intelligence is configurable

Under the hood, every character runs the same pure-Python minimax engine. What changes is the depth (1–4 plies), randomness factor (0–40%), and think time (0.2–3s). Difficulty is a dial, not a different model.

characters.py
"kasparov": {
    "voice": {"provider": "inworld", "identifier": "Ashley"},
    "engine": {"depth": 4, "randomness": 0.0,
              "think_ms": (1500, 3000)},
    "move_lines": ["Obviously, {}.",
                   "The only move: {}."],
    "win_line": "As expected.",
}

ELO 400 · Voice: Josh

Woody

Impulsive beginner. Thinks 0.2–0.6s. 40% random moves. "Is that good?"

ELO 800 · Voice: Sophie

Patzer

Coffee-shop casual. 25% random. "I'm feeling knight to F6."

ELO 1200 · Voice: Brian

Fritz

Club regular. Methodical. "After some thought, bishop to C4."

ELO 1500 · Voice: Luna

Vera

Tournament player. "Think carefully." 0.7–1.8s thinks.

ELO 1800 · Voice: Eleanor

Morphy

Sharp attacker. No randomness. "Naturally, queen to D5."

ELO 2100 · Voice: Alex

Tal

Master tactician. "The position demands bishop takes F7."

ELO 2400 · Voice: Ashley

Kasparov

Relentless grandmaster. 1.5–3s deliberation. "As expected."

ELO 2700 · Voice: Ryan

Deep Blue

Maximum engine strength. "Move calculated: rook to D1."

Design & interactivity

Five themes. Real pieces. Click and voice.

Three.js powers five environments: stars with nebulae, a torchlit dungeon with embers, frosted glass, a cyber grid, and warm dust motes. Lichess SVG pieces animate with spring physics.

Click-to-move sends UCI notation through sendTextEvent() — the same handler path as voice. The system doesn’t know which input method you used.

Two voices, one session

System messages use OpenAI Nova. Opponent moves use their character’s Inworld voice. You hear the difference between the game engine and your opponent instantly.

Human-like think times

Each character has a random think delay between 0.2 and 3 seconds — Woody blurts out a move in 200ms, Kasparov deliberates for up to 3 seconds. A SilenceEvent fills the gap between instant engine computation and the character’s persona.

PWA-ready

Service worker, manifest, safe-area insets. Add to home screen and it runs standalone.

What will you build?

Voice Chess went from empty directory to deployed game in one working session. A Solutions Architect can help you scope your own — or pip install voicerun-cli and start tonight.