A Tour of moonshine-go's 11 Runnable Voice Samples

For years, the classic voice cascade (Speech-to-Text → LLM → Text-to-Speech) was written off as too slow compared to monolithic speech-to-speech models. The cascade never lost on capability. It lost on milliseconds.

moonshine-go combines Moonshine's streaming STT models with pure-Go runtime bindings (ebitengine/purego) to eliminate cgo overhead and local IPC delays.

Once latency stops being the bottleneck, the cascade's original advantages return:

  • Control: Every stage is open to gate, swap, or configure.
  • Observability: Every utterance is an inspectable, loggable event.
  • Privacy: Audio dies at the microphone; only text you choose leaves the box.
  • Composability: The transcript acts as a bus any process can attach to in any language.

What You Can Build

Before diving into how the samples work under the hood, here are three real-world voice applications you can build on top of these patterns today:

1. Meeting or Field Copilot with Cited RAG

samples/go-cascade-faq demonstrates an offline voice agent answering questions over a local document index. Swapping StaticRetriever for a vector database or local search engine yields an ambient copilot for meetings, clinical dictation, or field service. Because retrieval runs as a named tool call with logged arguments, every spoken answer cites its exact source document in the event stream.

2. Voice-Controlled Terminal (voice-tmux) — Open Idea

This application does not exist in the repository yet: a standalone Go bridge that streams moonshine serve transcripts into tmux send-keys, enabling voice control over any terminal session. Dictation streams into the active pane by default, while fast-path verbs ("run it", "interrupt", "clear", "new window") execute control commands. We have this tracked as open epic moonshine-go-avc. Pick it up if you want to contribute a flagship sample.

3. Deterministic Hands-Free Control

For industrial inspection, sterile laboratory work, or accessibility tools, a voice command must be provable and repeatable. A probabilistic LLM might hallucinate a safety command or fail under noise. Fast-path intent matching (IntentMatcher or pkg/agentflow) intercepts fixed control verbs in under 1ms before any LLM evaluates the line.


The Latency Advantage

moonshine serve cuts latency at every stage of the cascade:

Stage Cloud Speech-to-Speech Moonshine Local Cascade
Audio Transport / Ingress 150ms – 350ms (cloud upload) 0ms – 5ms (local IPC / loopback WS)
STT Time-to-First-Token 300ms – 600ms 25ms – 75ms (tiny-streaming)
STT Line Finalization 400ms – 800ms 30ms – 90ms (LastLatencyMs)
Fast-Path Intent Execution Not available (requires full LLM turn) < 1ms (deterministic rule / AgentFlow)
RAG / Tool Call Round-Trip 400ms – 1000ms 10ms – 50ms (StaticRetriever / local RPC)
TTS First Audio Chunk 200ms – 500ms 15ms – 40ms (local Piper / Web Audio)
Total End-to-End Latency 1,050ms – 2,500ms 40ms – 120ms (fast-path) / 180ms – 450ms (full LLM)

Eliminating cloud network round-trips and running fast-path rules before invoking an LLM keeps control actions under 100ms, a speed cloud speech-to-speech cannot reach.


Every Sample Runs in CI

Every example in our samples/ directory is a standalone program tested in CI on every commit (./scripts/verify-samples.sh). A broken sample fails go build or python before anyone reads it.

The sample catalog spans three integration tiers plus in-process native embedding.


Tier 0: Subscribe to Live Transcripts in Any Language

Connecting to moonshine serve's transcript feed requires zero SDKs or custom codegen: just JSON over a WebSocket (ws://localhost:8765/ws) or gRPC (:9090).

samples/go-listen contains ~90 lines of Go with no dependencies on moonshine-go itself. It demonstrates the finalized-once idempotency contract: track processed line IDs from finalized_line_ids so interim frames dropped under backpressure do not cause duplicate output.

// samples/go-listen/main.go
seen := make(map[uint64]bool)
for {
    var env envelope
    if err := wsjson.Read(ctx, conn, &env); err != nil {
        return
    }
    if env.Kind != "transcript" {
        continue
    }

    var ev transcriptEvent
    json.Unmarshal(env.Payload, &ev)

    byID := make(map[uint64]line, len(ev.Lines))
    for _, l := range ev.Lines {
        byID[l.ID] = l
    }
    for _, id := range ev.FinalizedLineIDs {
        if !seen[id] {
            seen[id] = true
            if l, ok := byID[id]; ok {
                fmt.Printf("[FINAL] %s\n", l.Text)
            }
        }
    }
}

samples/python-listen implements the same pattern in ~40 lines of Python with standard websockets.


Tier 2 Flagship: Multi-Turn Voice Agents with pkg/agentflow

For Go developers building structured, multi-turn voice applications, samples/go-cascade-faq is our flagship sample. It runs a local voice agent that answers questions about moonshine-go's mission using local RAG. No network call, no LLM API key.

It uses pkg/agentflow, a Go-native voice agent DSL supporting trigger-phrase matching (ListenFor), global control handlers (Always), multi-turn dialogs (Say, Ask, Confirm, Choose), and fallback guidance (Otherwise):

// samples/go-cascade-faq/main.go
func newAgentFlow(sink serveapi.ActionSink) serveapi.AgentHandler {
    flow := agentflow.New()
    flow.ActionSink(sink)

    // Intercept control commands before FAQ logic runs
    flow.Always("stop listening", func(d *agentflow.Dialog) error {
        fmt.Println("[agent] heard 'stop listening' -- pausing session")
        _, err := d.PauseListening()
        return err
    })

    flow.Always("resume listening", func(d *agentflow.Dialog) error {
        fmt.Println("[agent] heard 'resume listening' -- resuming session")
        _, err := d.ResumeListening()
        return err
    })

    // FAQ conversation flows using local StaticRetriever
    flow.ListenFor("mission", func(d *agentflow.Dialog) error {
        results, err := retriever.Retrieve(context.Background(), "mission")
        if err != nil || len(results) == 0 {
            return nil
        }
        return d.Say(results[0].Snippet)
    })

    flow.Otherwise(func(utterance string) {
        fmt.Println("[agent] no match -- try: mission, privacy, or 'stop listening'")
    })

    return agentflow.NewHandlerAdapter(flow)
}

In-Process Mode: AgentFlow can also run inside the daemon via moonshine serve --agent agentflow --allow-actions without a separate process.


Native Embedding: Speech-to-Text as an MCP Tool

When an application does not run a daemon, pkg/moonshine provides in-process STT inference with zero cgo build toolchains.

samples/mcp-transcribe embeds pkg/moonshine inside an official Model Context Protocol (MCP) server. Claude Desktop, Cursor, or any MCP-compatible agent host can call the transcribe tool to process local .wav files in Go memory space:

# Run MCP server over stdio for Claude Desktop / Cursor
go run . -model-dir ./models/tiny-en

Summary of the Sample Catalog

Sample Tier Description
go-listen Tier 0 Real-time transcript feed in Go (~90 lines, zero moonshine-go deps)
python-listen Tier 0 Python twin of go-listen in ~40 lines
grpc-listen Tier 0 High-throughput gRPC transcript client via pkg/servepb protobufs
browser-listen Tier 1 Zero-install browser mic capture via --audio-source remote
browser-cascade-faq Tier 1 In-browser voice FAQ with Web Audio TTS playback in pure JS
go-cascade-faq Tier 1/2 Flagship: Offline Go RAG voice FAQ powered by pkg/agentflow
python-agent Tier 1 Python external agent sending speak / session.pause JSON actions
go-bulk-analysis Tier 2 Batch transcribes folders at ~50–70x real-time + LLM report pass
go-embedded Native Direct in-process batch & streaming STT without a daemon
mcp-transcribe Native Embedded MCP server exposing in-process transcribe tool
desktop-app Native Native Wails v2 desktop GUI app with in-process STT

Next Steps

  1. Read samples/TUTORIAL.md: Build an Offline Voice Agent in 60 Minutes (4-part guided walkthrough).
  2. Review samples/GUIDE.md for architecture deep-dives and pattern selection matrices.
  3. Check samples/CONTRIBUTING.md to add a sample to the repository.