Scribe — a fully native, notarized macOS dictation app, written in Crystal. Free, on-device, shipping today

We just shipped something I’ve wanted to exist for years, and I think this community will care about how it exists: Scribe is a complete consumer macOS app written in Crystal — menu bar app, global hotkeys, native windows, setup wizard, Apple Help Book, signed with a Developer ID, notarized by Apple, stapled, and distributed as a drag-to-Applications DMG. No Electron. No embedded browser. A 7 MB Crystal binary at the core.

Download it here (free, and free for life):

What it does

Tap/hold a hotkey, talk, and your words are typed at your cursor — in any app. Everything runs on-device on the Apple Neural Engine: no cloud, no account, no audio ever leaves your machine. It also does meeting transcription with speaker labels (captures system audio, so both sides of a call get transcribed), and drag-in transcription of existing audio files. Transcription runs at roughly 30x real-time on Apple Silicon.

The part I care about most

Here’s what I actually want this community to hear: I didn’t grind this out line by line. Scribe was vibe coded — built AI-first, agents doing the heavy lifting under direction — on tooling we built ourselves, for the language we love. The compiler is our own fork (crimson-knight/crystal, incremental-compilation branch, installed as crystal-alpha). Dependencies are managed by our shards fork (shards-alpha). The native capabilities come from our own shards: llamero (Parakeet ASR and TTS on the Neural Engine), our asset_pipeline fork (the SwiftUI facade), and crystal-audio (system-audio capture). Every layer of this app, from the compiler down, is tooling we can rely on because we own it.

And that’s the message: Crystal has a unique advantage in an AI-driven world that most stacks don’t. The type system and compiler give an AI agent the one thing it can’t fake — instant, truthful feedback. An agent can’t hand-wave past a compile error the way it can slip a subtle bug into a dynamic language; it has to actually get it right, and when Crystal compiles clean you’re most of the way to correct. Then the performance shows up for free. Type system, compiler, performance, together: the future is extremely bright and the opportunity is extremely ripe for everyone using Crystal.

Why I’m posting it here

Because the frontier just moved. The question “can you build a real Mac app in Crystal — the kind you hand to a non-technical person and it just works?” now has an existence proof, and it passed Apple’s notarization gauntlet on the first real attempt.

The interesting bits, briefly:

  • The whole app is ~7,600 lines of Crystal plus one ObjC bridge file (~1,700 lines) exposing AppKit, Carbon hotkeys, CGEvent typing, ScreenCaptureKit, notifications, and permissions as a flat C API that Crystal binds with a plain lib. That’s the entire platform layer. It’s less scary than it sounds — Crystal’s C interop makes this genuinely pleasant.
  • We talk to the Neural Engine from Crystal. Speech recognition is NVIDIA’s Parakeet v3 running via CoreML, wrapped in a Swift package that exports a C ABI (@_cdecl), which Crystal dlopens directly. Crystal → C → Swift → ANE, with results streaming back as JSON frames on the calling thread. We started on whisper.cpp; the Parakeet-on-ANE path was such a leap (faster, cooler, no GPU contention) that whisper is gone entirely.
  • The UI is SwiftUI, driven from Crystal through a facade layer, so views read like declarative Crystal (UI::VStack, UI::Toggle, forms) while rendering as native controls.
  • The fork’s threading-friendly runtime carries the audio path — native run loops and foreign-thread audio callbacks, with recording memory flat for hours. Perf gates enforce that on every release, so the agents can’t regress it either.

The point

Crystal is usually pitched for servers and CLIs. This is neither. It’s a signed, notarized, Gatekeeper-approved consumer app that ordinary people are downloading and using today — and the language never got in the way. If you’ve been wondering whether Crystal can carry a full native app end-to-end: it can, and you can hold the proof.

Try it, break it, tell me what you think. A pro version is coming later, but the dictation app you download today is complete and stays free forever.

  • Seth

I try to understand your readme, but it’s so cryptic :) so I ask AI to explain:


Scribe — Mac app that types what you say

What it does

Press a hotkey → speak → your words appear as text in any app (Word, browser, Telegram, etc.)

What makes it great for you

  • Free forever — no subscription, no hidden payments
  • Works offline — use it anywhere, even without internet
  • No registration — just download and use it instantly
  • Private — your voice never leaves your device. Nobody hears it, not even the developer
  • Lightweight — only 7 MB. Won’t slow down your Mac or drain your battery
  • Works everywhere — in any app on your Mac

What else it can do

  • Transcribes meeting recordings with speaker labels (identifies who said what)
  • Works with existing audio files — just drag and drop

Bottom line: A fast, private, and dead-simple dictation tool that just works. No accounts, no cloud, no subscription.


Is this accurate? If so, you should probably put something like this at the top of your README so non macOS developers can understand what the app actually does :)

Yeah it’s all true, and that’s a good suggestion I’ll go update that.

But it is only for MacOS so making that more clear would be a good idea as well lol

Looks nice. Is there any code that could be made into it’s own shard?

Great question — and it goes right to the heart of why Scribe exists. The app is intended to be a public working demonstration of how powerful the individual libraries I’ve been working on can be: a real, tangible, easy-to-use product that shows just how far you can get with them. Scribe itself is only ~7,600 lines of Crystal — nearly all the heavy lifting already lives in shards you can use today:

crystal-audio — audio capture in Crystal: microphone, system audio (no virtual audio driver needed on macOS 14.2+ — it uses AudioHardwareCreateProcessTap), or both simultaneously into separate files. That dual-stream mode is what powers Scribe’s meeting transcription — your voice and the call audio kept separate. On-device transcription is included too: whisper.cpp bindings with batch and near-real-time streaming modes, plus an optional LLM formatting pipeline. It has also grown multi-track playback, lock-screen media controls, and iOS/Android build targets.

llamero — the AI layer. It started as a llama.cpp wrapper and now has two tracks: a provider client (OpenAI / Anthropic / Groq / OpenRouter, with structured output parsed into Crystal objects) and a native Apple track — an MLX runtime that keeps a model resident on Apple Silicon, streams chat, hot-swaps LoRA adapters, and can even train a QLoRA adapter on-device from Crystal, no Python involved. The Parakeet speech-to-text runtime Scribe uses (Apple Neural Engine, ~30x real-time) lives here too, behind a small Swift bridge loaded via dlopen — with a deterministic mock fallback so specs and non-Apple development keep working.

asset_pipeline (the feature/utility-first-css-asset-pipeline branch) — beyond the classic web asset pipeline it now has a native UI layer: require "asset_pipeline/ui" gives you AppKit/UIKit renderers driven from Crystal — windows, SwiftUI-hosted forms, native components — backed by an auditable Apple HIG validation ledger. Every window in Scribe (settings, setup wizard, about) is built with it.

Amber V2 + Grant — Scribe is structured like an Amber application (config, models, Grant ORM over SQLite) with no HTTP server at all. The framework’s patterns surviving being pointed at a menu-bar app instead of the web was a nice little existence proof of its own.

crystal-alpha — the compiler itself, a fork of Crystal with incremental compilation (plus iOS/Android/watchOS target work and a --shared output mode). The fork comes with a promise: it maintains parity with what’s publicly available. Upstream released Crystal 1.21.0 on July 16, and the fork’s new release, v1.21.0-incremental-1, merges it — everything in current public Crystal, with the incremental machinery on top (on a Kemal test app a no-change rebuild returns in ~0.26s instead of ~2.2s, with ~6% cold-build overhead). The active branch is incremental-compilation; releases are cut from it on the releases page.

The other half of the story: this ecosystem is specifically designed for use with AI coding assistants, so the learning curve of the engineer/architect is reduced. The libraries ship their own agent context — CLAUDE.md, skills, workflows — and shards-alpha (my shards fork, brew install crimson-knight/tap/shards-alpha) is the delivery mechanism: on install, the AI docs from your dependencies land in your project’s .claude/ directory, namespaced per shard and version-tracked so your own customizations survive updates. It also handles MCP server distribution and supply-chain compliance (OSV vulnerability audits, license checks, SBOM generation). Point your assistant at a fresh project and it already knows how to drive these libraries.