Copyright 2025-2026 Ardan Labs
This project lets you use Go for hardware accelerated local speech-to-text with whisper.cpp directly integrated into your applications. Bucky provides a high-level API that mirrors whisper.h 1-to-1 plus pure-Go audio decoding so you can hand any WAV/MP3/FLAC file to a model and get a transcript back.
Bucky is the speech-to-text sibling of hybridgroup/yzma (which binds llama.cpp). The end goal is to give Kronk a native, OpenAI-compatible POST /v1/audio/transcriptions endpoint without the CGo toolchain.
Bucky is the squirrel from The Emperor's New Groove — he speaks in squeaks/whispers. Naming a Whisper binding after him is just good taste.
To install bucky, fetch the whisper.cpp shared libraries, and transcribe the bundled JFK sample:
$ go install github.com/ardanlabs/bucky@latest
$ bucky install -lib ./lib
$ export BUCKY_LIB=$(pwd)/lib
$ bucky model get tiny
$ bucky whisper transcribe -m ~/models/ggml-tiny.bin samples/jfk.wavRead INSTALL.md for per-OS install notes and MODELS.md for the recommended whisper model set.
Sometimes there are breaking changes to whisper.cpp that require an update to bucky. Here are the known compatible versions:
| whisper.cpp | bucky |
|---|---|
| v1.8.4 | 0.1.x |
The core FFI binding (model loading, whisper_full, segments + tokens, VAD, state, language, bench helpers), audio decoding (WAV/MP3/FLAC), CLI (install, system, model get|info|list, whisper transcribe), and examples (hello, transcribe, translate, segments, words, streaming) have all landed. Kronk integration (an OpenAI-compatible POST /v1/audio/transcriptions endpoint) lives in the kronk repo.
Name: Bill Kennedy
Company: Ardan Labs
Title: Managing Partner
Email: bill@ardanlabs.com
BlueSky: https://bsky.app/profile/goinggo.net
LinkedIn: www.linkedin.com/in/william-kennedy-5b318778/
Twitter: https://x.com/goinggodotnet
The fastest way to install on any supported platform is with Go:
$ go install github.com/ardanlabs/bucky@latest
$ bucky --helpThen fetch the whisper.cpp shared library bundle (xcframework on darwin, DLLs on windows, .tar.gz from ardanlabs/bucky-builder on linux):
$ bucky install -lib ./lib
$ export BUCKY_LIB=$(pwd)/lib
$ bucky systemAnd pull a model from the bundled catalog:
$ bucky model list
$ bucky model get tiny
$ bucky model info -m ~/models/ggml-tiny.binHere is the existing Issues/Features for the project and the things being worked on or things that would be nice to have.
If you are interested in helping in any way, please send an email to Bill Kennedy.
The architecture of bucky mirrors yzma file-for-file so anyone who knows yzma can drop straight in. There is no CGo: every C call goes through purego + JupiterRider/ffi.
┌─────────────────────────────────────────────────────────────┐
│ cmd/ bucky CLI (install, system, model, whisper) │
├─────────────────────────────────────────────────────────────┤
│ pkg/whisper 1-to-1 mirror of whisper.h │
│ (model, context, full, segments, tokens, │
│ lang, state, vad, bench, params) │
│ pkg/audio pure-Go decoders (WAV / MP3 / FLAC) + │
│ downmix-to-mono + 16 kHz resampling │
│ pkg/loader BUCKY_LIB-aware purego library loader │
│ pkg/download go-getter-driven release-archive resolver │
│ pkg/utils cross-platform Go ↔ C string helpers │
└─────────────────────────────────────────────────────────────┘
│
▼
libwhisper.{dylib|so|dll}
(whisper.cpp v1.8.4)
Bucky uses GGML-format models supported by whisper.cpp. The official set lives at ggerganov/whisper.cpp on Hugging Face (tiny / base / small / medium / large-v3 / large-v3-turbo, plus -en English-only and quantized -q5_0 / -q8_0 variants). Companion VAD models live at ggml-org/whisper-vad.
Bucky ships a small bundled catalog so you can bucky model get tiny instead of pasting a URL:
$ bucky model list
$ bucky model get tiny
$ bucky model get -u https://example.com/foo.bin -o ~/models # arbitrary URL
$ bucky model get silero-vadSee MODELS.md for the recommended set with size / speed / quality trade-offs.
Bucky uses the prebuilt whisper.cpp release artifacts where they exist; Linux artifacts come from the ardanlabs/bucky-builder companion repo (whisper.cpp upstream publishes no Linux release).
| OS | CPU | GPU | Source |
|---|---|---|---|
| Linux | amd64, arm64 | CUDA 12.9, Vulkan | whisper-vX.Y.Z-bin-ubuntu-{cpu,cuda,vulkan}-{x64,arm64}.tar.gz (bucky-builder) |
| macOS | arm64, amd64 | Metal | whisper-vX.Y.Z-xcframework.zip (upstream) |
| Windows | amd64 | CPU, CUDA 12 | whisper-bin-x64.zip / -cublas-… (upstream) |
Whenever there is a new release of whisper.cpp, the FFI struct mirrors and pkg/download matrix may need a refresh. The pinned version is captured in pkg/download/.
There are examples in the examples/ directory. Each one
expects BUCKY_LIB and BUCKY_TEST_MODEL to be set:
$ export BUCKY_LIB=$(pwd)/lib
$ export BUCKY_TEST_MODEL=$HOME/models/ggml-tiny.binHELLO — the smallest possible bucky program: load a tiny model, decode an audio file, print the transcript.
$ make helloTRANSCRIBE — fuller transcription example with -lang, -prompt, -temperature, and -beam flags.
$ go run ./examples/transcribe -lang en samples/jfk.wavTRANSLATE — sets wparams.Translate = 1 to translate non-English audio to English.
$ go run ./examples/translate -m $HOME/models/ggml-base.bin some-foreign-audio.wavSEGMENTS — print each generated segment with [mm:ss.mmm -> mm:ss.mmm] text. Pass -tokens for per-token detail.
$ go run ./examples/segments -tokens samples/jfk.wavWORDS — enable token-level timestamps and print [t0 -> t1] word for every emitted token. Documents the experimental DTW path in a comment.
$ go run ./examples/words samples/jfk.wavSTREAMING — sliding-window pseudo-streaming over the input file. Each window's tail tokens are carried into the next via wparams.PromptTokens.
$ go run ./examples/streaming samples/jfk.wav// hello is the smallest possible bucky example: load a tiny whisper model,
// decode an audio file (WAV / MP3 / FLAC), and print the resulting text.
package main
import (
"fmt"
"log"
"os"
"strings"
"github.com/ardanlabs/bucky/pkg/audio"
"github.com/ardanlabs/bucky/pkg/whisper"
)
func main() {
if len(os.Args) < 2 {
log.Fatalf("usage: %s <audio-file>", os.Args[0])
}
audioPath := os.Args[1]
libPath := os.Getenv("BUCKY_LIB")
if libPath == "" {
log.Fatal("BUCKY_LIB must point to the directory containing libwhisper")
}
modelPath := os.Getenv("BUCKY_TEST_MODEL")
if modelPath == "" {
log.Fatal("BUCKY_TEST_MODEL must point to a GGML whisper model (e.g. ggml-tiny.bin)")
}
if err := whisper.Load(libPath); err != nil {
log.Fatalf("whisper.Load: %v", err)
}
if err := whisper.Init(libPath); err != nil {
log.Fatalf("whisper.Init: %v", err)
}
cparams := whisper.ContextDefaultParams()
ctx, err := whisper.InitFromFileWithParams(modelPath, cparams)
if err != nil {
log.Fatalf("InitFromFileWithParams: %v", err)
}
defer whisper.Free(ctx)
f, err := os.Open(audioPath)
if err != nil {
log.Fatalf("open %s: %v", audioPath, err)
}
defer f.Close()
samples, err := audio.Decode(f)
if err != nil {
log.Fatalf("audio.Decode: %v", err)
}
wparams := whisper.FullDefaultParams(whisper.SamplingGreedy)
wparams.PrintProgress = 0
wparams.PrintRealtime = 0
wparams.PrintTimestamps = 0
wparams.NoTimestamps = 1
if err := whisper.Full(ctx, wparams, samples); err != nil {
log.Fatalf("Full: %v", err)
}
var sb strings.Builder
for i := int32(0); i < whisper.FullNSegments(ctx); i++ {
sb.WriteString(whisper.FullGetSegmentText(ctx, i))
}
fmt.Println(strings.TrimSpace(sb.String()))
}This example produces the following output:
$ make hello
go run ./examples/hello samples/jfk.wav
And so my fellow Americans ask not what your country can do for you ask what you can do for your country.Apache-2.0 — see LICENSE.