npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coroboros/scrybe

v0.2.0

Published

Collapse sound into signal — a fast, offline Whisper transcription & speaker-diarization CLI. No Python, no account.

Downloads

640

Readme

scrybe

Collapse sound into signal — a fast, offline Whisper transcription & speaker-diarization CLI. No Python, no account.

Pure-Rust audio decode, whisper.cpp via whisper-rs, Metal on Apple Silicon and CPU everywhere. Point it at one file or a whole folder and get text — and who spoke — back from the terminal. No system ffmpeg.

crates.io ci license stars skills coroboros.com

Contents

Requirements

  • macOS (Apple Silicon or Intel), Linux, or Windows.
  • A few hundred MB to ~8 GB of free RAM, depending on the model. scrybe auto-selects the largest model that fits detected RAM, so it runs on small machines and scales up on large ones.
  • From a prebuilt binary — nothing else. The binary embeds whisper.cpp and the voice-activity model.
  • From source — a C/C++ toolchain and CMake (whisper.cpp is built by whisper-rs-sys). Apple Silicon adds the Metal backend with --features metal. Diarization's ONNX Runtime downloads a prebuilt static lib on most targets; on Intel macOS and glibc-2.35 Linux it must be self-built (ORT_LIB_PATH) — the prebuilt binaries already ship it, so prefer those there.

Install

brew install coroboros/tap/scrybe   # macOS — the blessed path
npx @coroboros/scrybe               # Node toolchains
cargo binstall coroboros-scrybe     # prebuilt binary via cargo

From source (any platform; Apple Silicon adds the Metal backend):

cargo install --path .                   # CPU
cargo install --path . --features metal  # Apple Silicon (Metal)

A raw GitHub-release download on macOS may be quarantined — clear it with xattr -d com.apple.quarantine ./scrybe. The Homebrew, npx, and cargo paths are not quarantined.

Usage

scrybe ./recordings           # transcribe a folder
scrybe talk.mp3 --format srt  # one file, SubRip output
scrybe talk.mp3 --json        # stream a JSON transcript to stdout
scrybe call.wav --diarize     # label who spoke, in every format
scrybe panel.mp3 --diarize --speakers 3   # pin the speaker count
scrybe --dry-run ./in         # resolve the plan without transcribing
scrybe models list            # show the model family, sizes, cache status
scrybe models pull large-v3   # pre-fetch a model
scrybe --offline ./in         # cached models only, no network

Run scrybe --help for the full flag list.

Why this exists

Running Whisper from the terminal usually means a Python environment, a system ffmpeg, and a separate step to convert audio to 16 kHz WAV before the model ever sees it. scrybe collapses that into one binary.

  • Offline and private. Models download once, verified against a pinned SHA-256, into the standard Hugging Face cache. After that, nothing leaves the machine — --offline enforces it.
  • No Python, no system ffmpeg. A single Rust binary decodes mp3, wav, flac, ogg, and m4a natively via symphonia, resamples to 16 kHz mono, and runs whisper.cpp through whisper-rs.
  • Metal-accelerated on Apple Silicon, CPU everywhere else. The default build needs no GPU toolchain.
  • Zero-config. Omit --model and --jobs and scrybe picks the largest model and the concurrency that fit detected RAM, so a flag-free run is never refused by its own memory guard.
  • Fails loud, never silent. Voice-activity segmentation is always on as a correctness floor. Unsupported codecs, out-of-memory runs, and output collisions stop with an actionable message and a stable exit code rather than emitting garbled or overwritten files.

Models

| Model | Notes | |-------|-------| | tiny / base / small | small and fast, lower accuracy | | large-v3 | most accurate, translation-capable | | large-v3-turbo | default — near-large-v3 accuracy, much faster | | distil-large-v3.5 | distilled, fast, English-only (rejects a non-English --lang) |

Weights are ggml builds from the whisper.cpp Hugging Face repos. Only large-v3 translates to English (--task translate); the gate rejects the others before any download. With --model omitted, scrybe resolves the largest model that fits detected RAM at the chosen job count.

--diarize adds a pair of small ONNX models (pyannote segmentation-3.0 + a wespeaker speaker embedding, ~31 MB total), fetched from ungated Hugging Face mirrors with the same pinned-SHA-256 verification — no account, no token. Pre-fetch them with scrybe models pull diarization; they resolve before any transcription starts, so a missing model fails fast.

On an Apple M1, tiny transcribes at ~31×RT on CPU and large-v3-turbo at ~3×RT, rising to ~8×RT on Metal. Full numbers and method: bench/baseline.md.

Codecs

Decoded natively, no system ffmpeg required:

| Extension | Codec | |-----------|-------| | wav | PCM | | mp3 | MP3 | | flac | FLAC | | ogg / oga | Vorbis | | m4a / mp4 / m4b | AAC-LC, ALAC |

HE-AAC/SBR is not handled by the built-in decoder — it fails with exit code 10 rather than emit garbled audio. Re-encode with ffmpeg, or pass --decoder ffmpeg to decode through a system ffmpeg when one is on PATH.

Both decoders stream straight to 16 kHz mono, so the full-resolution source is never held in memory — a long, high-bitrate file (an hour of 48 kHz stereo) decodes fine. Only the 16 kHz output is resident, bounded at ~4.6 hours per file; a longer single clip fails loud with exit code 10, so split marathon recordings first.

Options

Every flag; scrybe --help prints the same surface.

| Option | Default | Description | | --- | --- | --- | | <paths>... | (required) | Audio files or directories to transcribe. | | --recursive | false | Recurse into subdirectories. | | --model <MODEL> | largest that fits RAM | Whisper model. See Models. | | --lang <LANG> | auto-detect | Source language code (en, fr, …). | | --task <TASK> | transcribe | transcribe or translate (to English). | | --format <FMT,…> | txt | Output formats, comma-separated. See Output formats. | | --out-dir <DIR> | beside input | Write outputs here instead of next to each input. | | --jobs <N> | device-aware | Files decoded concurrently ahead of inference. | | --threads <N> | device-aware | CPU threads per inference job. | | --force | false | Reprocess inputs even when an up-to-date output exists. | | --dry-run | false | Print the resolved plan without transcribing. | | --decoder <BACKEND> | symphonia | symphonia (built-in) or ffmpeg (system). | | --diarize | false | Label who spoke; speakers land in every output format. | | --speakers <N> | auto | Exact speaker count (requires --diarize). | | --json | false | Force JSON; stream to stdout for one file, .json sidecars for many. | | --offline | false | Use only cached models; never access the network. | | --no-color | false | Disable colored output. |

| Command | Description | | --- | --- | | models list | List the model family, sizes, and which are cached. | | models pull <MODEL> | Download a model into the cache. | | models remove <MODEL> | Remove a cached model. | | models path | Print the model cache directory. |

scrybe models list
scrybe models pull large-v3-turbo
scrybe models path     # → ~/.cache/huggingface/hub

The cache honors HF_HOME. Downloads are resumable and verified against a pinned SHA-256; a corrupt cache entry is re-fetched once, or rejected under --offline.

Output formats

--format accepts any comma-separated combination; --json overrides it. Outputs land beside each input (talk.mp3talk.srt) or in --out-dir. An up-to-date output is skipped unless --force, and two inputs that would write the same file stop the run rather than overwrite.

| Format | Extension | Contents | | --- | --- | --- | | txt | .txt | One segment per line; Speaker 1: prefixes with --diarize. | | srt | .srt | SubRip cues with HH:MM:SS,mmm timing; Speaker 1: prefixes with --diarize. | | vtt | .vtt | WebVTT cues with HH:MM:SS.mmm timing; <v Speaker 1> voice tags with --diarize. | | tsv | .tsv | start, end (milliseconds), text columns; a speaker column with --diarize. | | csv | .csv | start, end (milliseconds), text — RFC 4180 quoted; a speaker column with --diarize. | | json | .json | Stable versioned schema — model, language, duration, segments. |

Subtitle timestamps are sanitized: never negative, never overlapping. JSON carries a schema_version so downstream tooling can pin it.

Speaker labels differ by audience: the human formats (txt, srt, vtt) read Speaker 1, Speaker 2, … while the machine formats (json, tsv, csv) use SPEAKER_00, SPEAKER_01, … — WhisperX-compatible and zero-indexed, so the same speaker is Speaker 1 in a subtitle and SPEAKER_00 in the JSON.

{
  "schema_version": 1,
  "model": "large-v3-turbo",
  "language": "en",
  "duration": 12.84,
  "segments": [
    {
      "start": 0.0,
      "end": 2.4,
      "text": "the quick brown fox",
      "speaker": "SPEAKER_00",
      "words": [
        { "start": 0.0, "end": 0.5, "text": "the", "speaker": "SPEAKER_00" },
        { "start": 0.5, "end": 1.1, "text": "quick", "speaker": "SPEAKER_00" }
      ]
    }
  ]
}

Timestamps are in seconds. Each segment carries a words array of per-word timing, emitted only with JSON output (the other formats carry segment-level timing only). speaker labels (SPEAKER_00, WhisperX-compatible) appear per segment and per word with --diarize. Both fields are additive and optional — absent without --diarize or on a word-less segment — so schema_version stays 1.

Agents

scrybe ships an agent skill — its own usage guide — for coding agents. Install it into your agent:

npx skills add coroboros/scrybe

Or read it inline without installing:

scrybe skills get scrybe   # print the bundled skill to stdout
scrybe skills list         # list bundled skills

scrybe --help carries an Agents: footer with the same pointers. The skill installs the scrybe binary if it is not already on PATH, then drives the CLI for transcription, batch folders, and machine-readable --json output. The same Markdown is the single source — embedded in the binary for skills get and published for npx skills add.

Exit codes

Stable across releases — only ever added, never renumbered.

| Code | Meaning | |------|---------| | 0 | success | | 1 | unexpected error (e.g. failed to write output) | | 2 | usage error (bad flag or value) | | 10 | unsupported codec | | 11 | model download failed | | 12 | out of memory | | 13 | GPU init failed | | 14 | file not found | | 15 | model load failed (corrupt or incompatible ggml) | | 16 | transcription failed (compute or decode failure) | | 17 | speaker diarization failed | | 20 | partial batch failure, or interrupted before completion |

Limitations

  • Per-file length ceiling — decode streams to 16 kHz mono, so the source size is unbounded, but the resident output caps at ~4.6 hours per file (exit 10 beyond that). Split marathon recordings.
  • HE-AAC/SBR — the built-in decoder rejects it rather than mis-decode. Use --decoder ffmpeg or re-encode.
  • Diarization trades peak accuracy for zero setup--diarize runs the pyannote 3.1 recipe fully offline with agglomerative clustering; on hard audio (heavy overlap, many similar voices) a tuned pyannote/WhisperX Python setup can still edge it out. Segments keep one dominant speaker; per-word labels in JSON carry the detail. Clustering memory grows with the recording's speech length (as in pyannote), so a multi-hour file wants a few GB of headroom.
  • GPU backends build from source — Metal ships in Apple Silicon prebuilts; cuda and vulkan are opt-in cargo features built locally.

Compared to alternatives

| Feature | openai-whisper | whisper.cpp (cli) | faster-whisper | WhisperX | scrybe | | --- | :---: | :---: | :---: | :---: | :---: | | Runtime | Python + PyTorch | C/C++ | Python | Python | Rust | | No Python required | no | yes | no | no | yes | | Single self-contained binary | no | yes (after build) | no | no | yes | | Native multi-codec decode, no system ffmpeg | no | no (16 kHz WAV / ffmpeg) | no | no | yes | | Apple Silicon GPU (Metal) | no (CPU/CUDA) | yes | no (CPU/CUDA) | no (CUDA) | yes | | Folder/batch with progress + summary | no | no | no | no | yes | | Output txt/srt/vtt/json/tsv/csv | yes (no csv) | yes (no tsv) | via wrapper | yes (no csv) | yes | | Zero-config model + concurrency | no | no | no | no | yes | | Stable exit-code contract | no | no | no | no | yes | | Word-level timestamps | yes | yes | yes | yes (alignment) | yes (JSON) | | Speaker diarization | no | no | no | yes (HF token) | yes — offline, no account |

scrybe's niche is a single self-contained binary that decodes any common codec, batch-transcribes offline, and labels who spoke — with Metal acceleration, no Python environment, no system ffmpeg, and no Hugging Face account. The Python tools — openai-whisper, faster-whisper, and WhisperX — require a Python environment, a system ffmpeg, and usually CUDA; WhisperX diarization additionally needs an HF token with gated-model access, and its tuned pipeline can still win on the hardest multi-speaker audio. whisper.cpp is the engine scrybe embeds; its own CLI expects pre-converted 16 kHz WAV (or an ffmpeg-enabled build), and leaves model selection, codec decode, batch UX, and output formatting to the caller. scrybe adds those on top.

Contributing

Bug reports and PRs welcome.

  • Open an issue before submitting non-trivial PRs.
  • Commits follow Conventional Commits.
  • Run cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test before pushing.
  • Target the main branch.

License

MIT