hark is the verb — "listen and transcribe." It captures your microphone,
all system audio, or specific apps on macOS and turns it into a transcript —
from a single native Swift binary, no drivers, no Electron, no network calls by
default. It's built the Unix way: do one thing well, one input → the outputs
you name, and clean stdin/stdout streaming that composes with ffmpeg, sox,
and your shell. Transcribe on-device with your pick of engines — whisper.cpp,
Apple Speech, WhisperKit, or Parakeet.
hark # live mic → transcript on stdout
hark --system --mix -a meeting.m4a -t meeting.srt # record a call, keep audio + subtitles
hark -i recording.m4a # transcribe a file⭐ If hark saves you time, please star it — stars help it qualify for a tap-free
brew install.
Several of these pipe hark straight into
fabric-ai — hark turns sound into
text, fabric turns text into summaries, action items, and insights
(brew install fabric-ai).
Watch live captions while you record — see the transcript stream in your terminal and save it to a file at the same time (pause, mute, or yank as you go).
hark --interactive --system --mix -t meeting.txtSummarize a meeting with AI — capture the call + your mic, then let fabric write the notes, decisions, and action items.
hark --system --mix -t - | fabric-ai -p summarize_meetingHands-free notes for your Google Meet calls — a browser userscript drives hark's remote-control agent: it starts a recording when you join a call and stops when you leave, naming the file from the meeting title. Zero clicks (record with everyone's consent — see Legal & responsible use).
hark --remote-control 8473 -C ~/Recordings # then add the Tampermonkey script (docs/remote-control.md)Dictate straight to your clipboard — speak, get text, paste anywhere.
hark --engine apple -t - | pbcopyMine a talk or podcast for insights — pull the key ideas, quotes, and references out of any recording.
hark -i podcast.mp3 -t - | fabric-ai -p extract_wisdomCapture a quick voice memo — timestamped audio + transcript in one shot.
hark -a memo.m4a -t memo.txtTranscribe a file to subtitles or JSON — any format in, .srt/.json out.
hark -i lecture.m4a -t lecture.srtLabel who said what — separate your mic from the call (You / Others), plus
per-voice Speaker N.
hark --system --mix --speakers -t meeting.srtMore copy-and-adapt wrappers live in examples/ — including
hark-meeting, which records a call and runs it through fabric-ai for you.
Note
Status: pre-1.0 beta. Core capture, transcoding, and transcription work; packaging polish is ongoing. See PLAN.md and CHANGELOG.md.
- One native binary. Pure Swift + Core Audio. No BlackHole, no virtual
devices, no background apps —
brew installand go. - Capture anything. The microphone, all system audio (
--system), specific apps (--app), or everything except some apps (--exclude-app) — and optionally mix in your mic (--mix) for meetings. - Live transcription. Text streams as you speak, with a choice of engines:
local
whisper.cpp, on-device Apple Speech, WhisperKit, or Parakeet. - Know who spoke.
--speakerslabels turns by source (You/Others) and by voice (Speaker 1..N) using on-device CoreML — no cloud. - Private by default. Everything runs on-device; no telemetry, no network calls unless you download a model.
- Built for pipelines.
-a -streams WAV to stdout, transcripts go to stdout, diagnostics to stderr — composes withffmpeg,sox, and friends. - Unattended-ready. Auto-recovers from screen lock / sleep / device changes,
--keep-awake, durations, splitting, and a remote-control HTTP agent.
- Requirements
- Install
- Quick start
- Usage
- Transcription engines
- Speaker labels
- Interactive mode
- Remote control
- Models
- Configuration
- Permissions
- Pipelines
- Recipes
- Development
- Project documents
- Legal & responsible use
- License
- macOS 14.4 (Sonoma) or later — required for the Core Audio process-tap API. Apple Silicon and Intel.
- The optional ScreenCaptureKit backend (
--capture-backend sckit) needs macOS 15 (Sequoia) or later; on 14.x hark uses the Core Audio backend automatically. - For the
whisperengine: a whisper.cpp binary onPATH(brew install whisper-cpp) and a ggml model. - For the
appleengine: the Speech Recognition permission (granted on first use). No model download.
brew tap PhantomYdn/hark https://github.com/PhantomYdn/hark
brew install harkThis installs the prebuilt arm64 binary and the man page, and pulls in
whisper-cpp for the default engine. Homebrew downloads aren't quarantined, so
no Gatekeeper steps are needed.
Some Homebrew setups prompt you to trust a third-party tap — run
brew tapas above (andbrew trust phantomydn/harkif asked). The fully-qualified namephantomydn/hark/harkalso works.
With the Swift toolchain (Swift 6 / Xcode 16):
git clone https://github.com/PhantomYdn/hark.git hark && cd hark
make build # or: swift build -c release
cp .build/release/hark /usr/local/bin/harkThe arm64 binary is also attached to each
GitHub Release. It is signed
(Developer ID) and notarized, so it passes Gatekeeper on download — no xattr
workaround needed. The stable signature also means privacy grants persist across
upgrades instead of resetting on each new binary.
Then set up an on-device transcription model:
hark models download parakeet:v3 --default # on-device CoreML; sets engine=parakeetParakeet runs fully on-device (Apple Silicon) and needs no extra tools. Prefer
whisper.cpp — or on Intel — instead? Use
brew install whisper-cpp && hark models download base.en --default.
hark # live mic -> transcript on stdout (Ctrl+C to stop)
hark -i recording.m4a # transcribe a file -> stdout
hark -a rec.m4a # record only (no transcript)
hark -a rec.m4a -t notes.txt # record + transcribe to files
hark -i in.wav -a out.flac # transcode between formatshark takes one input and writes the outputs you name; naming no
output transcribes to stdout.
| Pick one input | Name the outputs | |
|---|---|---|
| default | system default microphone | transcript on stdout |
| flags | -d device · --system · --app · --exclude-app · --mix · -i file/stdin |
-a audio · -t transcript (- = stdout) |
hark --app com.apple.Music -a song.m4a # capture one app
hark --system --exclude-app com.zoom.xos -t - # everything but Zoom → stdout
hark -i talk.wav -a talk.mp3 -t talk.srt # transcode + subtitle a file
hark --duration 30 --split silence=2 -a memo.wav # split on 2s of silenceFull flag, environment & config reference →
The complete input/output tables, capture/timing flags, interruption recovery,
working-directory handling, and the configuration matrix live in
docs/reference.md. Run hark --help for the canonical
list and hark help <subcommand> for a subcommand's options.
Select with -e/--engine (default whisper). All engines accept any readable
input; it is normalized to 16 kHz mono internally.
| Engine | Runtime | Languages | Auto-detect | Translate→EN | Notes |
|---|---|---|---|---|---|
whisper (default) |
whisper.cpp binary | ~99 | yes (--language auto) |
yes | needs a non-.en model for non-English |
apple |
native Speech.framework (no deps) |
~50 locales | no (uses the locale) | no | on-device; plain-text only in batch |
whisperkit |
WhisperKit CoreML | ~99 | yes | yes | Apple-Silicon-first; models auto-download |
parakeet |
FluidAudio CoreML | 25 European (v3) / English (v2) | yes | no | Apple-Silicon-first; --model v2/v3 |
cloud |
post-MVP | — | — | — | — |
whisperis found onPATH(whisper-cli/whisper-cpp, andwhisper-serverfor resident live transcription) or via$HARK_WHISPER_BIN/$HARK_WHISPER_SERVER_BIN. Disable the server with$HARK_WHISPER_SERVER=0.appleneeds the Speech Recognition permission and runs entirely on-device. Batch transcription writes plain text; for.srt/.jsonfrom a file, use another engine. Live.srt/.jsonworks with any engine.whisperkitandparakeetare CoreML engines (Apple Silicon only). They download their models from Hugging Face on first use, then run fully on-device.parakeetauto-detects its language (--languageis ignored) and cannot translate.
--speakers (alias --diarize) labels each transcript segment with who spoke,
two ways that combine:
- Source attribution — in a meeting capture (
--mixwith--system/--app), your microphone is labeledYouand the call audioOthers. Deterministic, no model, works on Intel and headless. - Acoustic diarization — distinct voices within a stream are separated into
Speaker 1,Speaker 2, … using on-device CoreML models (Apple Silicon).
By default a meeting is labeled You + Speaker 1..N. Use
--speaker-mode source for the cheap You/Others split with no diarization.
# Live meeting: You + Speaker 1/2/… in real time
hark --system --mix --speakers -t meeting.srt
# Accurate offline pass (transcript written when you stop)
hark --system --mix --speakers --diarize-engine offline -t meeting.srt
# Diarize a recording (everyone becomes Speaker N — "You" is live-only)
hark -i meeting.wav --speakers -t out.jsonThe label appears per format: txt Speaker 1: …, srt [Speaker 1] …, json a
"speaker" field. Acoustic diarization is Apple-Silicon-only (on Intel,
diarized modes fall back to You/Others); the first use downloads a CoreML
model — pre-fetch with hark models download fluidaudio:diarizer. See the
full flag table for --speaker-mode,
--diarize-engine, --max-speakers, --speaker-threshold, and
--speaker-labels.
--interactive runs a live capture in a minimal terminal UI: the transcript
streams to the terminal, a startup status line shows the resolved
engine/source/format, and single keys control the session:
- space — pause / resume (the paused interval is not recorded)
- m — mute / unmute the microphone (shown only when a mic is in the capture; only the mic is silenced — system audio keeps recording and the timeline is preserved)
- y — yank: copy the transcript captured so far to the clipboard (local only)
- Enter — finish and finalise the file (Ctrl-C also stops)
hark --interactive --system --mix -a meeting.m4a # watch the transcript live
hark --interactive --system --mix -a meeting.m4a -t meeting.txt # …and persist itInteractive mode needs a real terminal (stdin + stdout are a TTY) and can't be
combined with -i or stdout output (-a -/-t -).
Let your browser drive recording. hark --remote-control [host:]port runs
Hark as a control agent (no capture on launch), exposing a small HTTP/JSON
API so a script — or a browser userscript — can start/pause/resume/stop/query a
recording. The headline use: a Tampermonkey userscript for hands-free notes on
your Google Meet calls — it POSTs /start when you join a call (filename
derived from the meeting title) and /stop when you leave, with no clicks
(record with everyone's consent — see
Legal & responsible use). The ready-to-use script and
full API reference are in docs/remote-control.md.
hark --remote-control 8473 -C ~/Recordings # loopback agent, recordings under ~/Recordings
hark --remote-control # bind 127.0.0.1 on the configured port (default 8473)
curl -s -X POST http://127.0.0.1:8473/start \
-d '{"system":true,"mix":true,"audio":"call.m4a","transcript":"call.srt"}'
curl -s http://127.0.0.1:8473/status
curl -s -X POST http://127.0.0.1:8473/mute # silence only the mic (mix capture)
curl -s -X POST http://127.0.0.1:8473/unmute
curl -s -X POST http://127.0.0.1:8473/stopBound to loopback by default; a non-loopback bind requires $HARK_REMOTE_TOKEN.
The address is optional: omit it to bind loopback on the remote-control-port
config key (default 8473; also $HARK_REMOTE_CONTROL_PORT).
Run it as a background service. If you installed via Homebrew, brew services start hark runs the agent as a per-user LaunchAgent (auto-starts at login) so the
userscript can reach it without keeping a terminal open. It binds the
remote-control-port config key and writes recordings under the directory
config key — set both with hark config (directory must be absolute). On
macOS 26 the first start needs one
launchctl kickstart gui/$(id -u)/homebrew.mxcl.hark, and system-audio capture
needs a one-time permission grant to the hark binary — see
docs/remote-control.md
and docs/permissions.md.
Whisper ggml models live in ~/.hark/models as ggml-<name>.bin; whisperkit
and parakeet CoreML models are cached by their SDKs (and shown by models list).
hark models list # installed models, all engines; default marked *
hark models list --available # downloadable catalog, with an ENGINE column
# Download names are engine-tagged: bare = whisper ggml, prefix = CoreML engine
hark models download large-v3-turbo # whisper ggml
hark models download whisperkit:large-v3-v20240930_626MB
hark models download parakeet:v3 # or parakeet:v2 (English-only)
hark models download fluidaudio:diarizer # speaker diarization (--speakers)The first whisper model you download becomes the default. --default makes any
model the default; for whisperkit/parakeet it also sets the engine.
Most defaults resolve flag › environment ($HARK_*) › config
(~/.hark/config.json) › built-in. Every setting has a flag, a $HARK_* env
var, and a config key.
hark config set engine apple
hark config set speaker-mode source
hark config show # every setting, its value, and its SOURCE
hark config path # where the JSON file livesThe config file is plain JSON and hand-editable. The complete settings matrix (every key, flag, env var, and default) is in docs/reference.md.
macOS gates microphone, system-audio, and speech recognition behind TCC. The
release binary is signed and notarized, so grants persist across upgrades; for a
shell-launched CLI, macOS may still attribute these prompts to the terminal
that launches hark.
System/app capture has two backends, selected by --capture-backend (default
auto, or $HARK_CAPTURE):
coreaudio— Core Audio process tap. Needs the narrower System Audio Recording permission and works headless (cron/launchd/SSH), macOS 14.4+.sckit— ScreenCaptureKit (SCStream, macOS 15+). Needs the broader Screen Recording permission and a graphical login session.
See docs/permissions.md for the exact System Settings paths, the system-audio "+" flow, and notes for tmux/screen.
hark -a - --duration 10 | ffmpeg -i - out.mp3 # stream live WAV into ffmpeg
hark -a - | hark -i - # record on one machine, transcribe on another
hark -t notes.txt --system & tail -f notes.txt # follow a live transcripthark follows POSIX conventions: audio/transcripts on stdout, diagnostics on
stderr (-v for detail), and a non-zero engine exit code propagates through the
pipeline. SIGINT/SIGTERM finalize the current file so it stays playable. See the
exit codes table for the sysexits(3) mapping.
Copy-and-adapt zsh wrappers for common workflows live in
examples/ — hark-meeting (interactive system+mic capture →
audio + transcript, then a fabric-ai summary), hark-note (quick voice memo),
and hark-dictate (speak → clipboard). See examples/README.md.
make build # swift build
make test # swift test (with a CLT Testing.framework path workaround)
make release # swift build -c release
make demo # render the README demo GIF (needs: brew install vhs ffmpeg)Modular SwiftPM targets: DeviceManager, TapEngine, Encoders, CLI. Many
integration tests are gated on optional tools (whisper.cpp, a model, say,
Speech authorization) and skip cleanly when absent. Contributions welcome — see
CONTRIBUTING.md.
- PRD.md — product requirements
- PLAN.md — phased implementation plan and status
- docs/reference.md — full flag, environment & config reference
- docs/permissions.md — TCC permission setup
- docs/remote-control.md — remote-control HTTP API
- docs/legal.md — export classification & responsible use
- CHANGELOG.md — release notes
hark records audio, so whether a given recording is lawful is up to you. Recording-consent rules vary by jurisdiction — some places require all-party consent, and recorded voice can be personal data under the GDPR. Make sure you have consent and a legal basis before recording calls, meetings, or other people.
hark is built to make that easy to do right: capture is consent-gated by macOS permissions (it can't record covertly), everything runs on-device, and there's no telemetry and no network access by default. On the export side, hark contains no proprietary cryptography — only ancillary OS-provided TLS for opt-in downloads — and is self-classified EAR99 as publicly-available open source. Details and the full classification note are in docs/legal.md (informational, not legal advice).
MIT © Ilya Naryzhnyy. Bundled third-party components are listed in NOTICES; MP3 output uses libmp3lame (LGPL-2.1).
