offmute: meeting transcription and diarization driven entirely by Gemini prompts
An experiment in meeting transcription and diarization with just an LLM. Maybe I went a little overboard though
At a glance
- What is it?
- offmute is a TypeScript CLI and module that sends audio, video and screenshots to Google's Gemini models and asks them to produce a diarized transcript plus a structured meeting report. It is an experiment, and the README treats it as one.
- Who is it for?
- Adopt offmute if you already have a Gemini API key, your recordings are ordinary meeting audio or video, and you want a transcript plus a structured report without standing up a local ASR pipeline. Skip it if you need word-level timestamps, deterministic speaker labels across sessions, or any processing that does not leave your machine: every stage here is a prompt to a hosted model.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 160 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem offmute targets: diarization without a diarization pipeline
Conventional meeting transcription stacks two or three separate systems: an ASR engine that emits words with timestamps, a speaker embedding model that clusters voice segments, and a post-processing step that assigns names to clusters. Each component has its own failure mode, and the speaker clustering step in particular degrades on short turns and overlapping speech. offmute removes that stack. The README describes it as an experiment in meeting transcription and diarization with just an LLM, and the repository description carries the same framing, plus a self-deprecating note that the author may have gone overboard. The intended user is someone who already has a meeting recording and a Gemini API key and wants a readable transcript with speaker turns and a summary, not an engineer building a speech pipeline. The trade-off is explicit in the design: you give up a local, inspectable ASR stage in exchange for a single hosted model call per chunk that produces text, speaker labels and speaker identification in one pass.
How the Gemini-only pipeline is wired: description, chunked transcription, report
The module exports three functions, and the README's usage example shows the data flow between them. generateDescription takes the input file and returns a description object; generateTranscription takes the input file plus that description and returns chunk transcriptions; generateReport takes description.finalDescription and the joined chunk transcriptions. The description stage is where video handling lives. The CLI exposes --screenshot-count with a default of 4, and the README states the tool extracts and analyzes visual information from video meetings, including understanding when demos are being displayed. So screenshots are sampled, described, and the resulting text is passed forward as context for the transcription prompt. Audio is split before transcription: --audio-chunk-minutes defaults to 10, and the intermediate directory layout shows separate audio and transcription folders, which implies per-chunk transcription files that are later concatenated. Speaker identification is a prompt-level behaviour, not a clustering algorithm. The README says offmute attempts to identify speakers by name and role when possible, and the module example passes userInstructions such as adding emotions and tone information for each speaker. Nothing in the supplied material describes a speaker embedding model, a voice activity detector, or a timestamp alignment step, so the diarization is whatever the model produces from the chunk it is given.
Getting it running: one environment variable and a model flag
The quick start is two commands. Set GEMINI_API_KEY in the environment, then run npx offmute path/to/your/meeting.mp4. The default model is Pro, described as Gemini 2.5 Pro for all operations. To switch, pass --model with one of pro, flash or flash-lite; the README labels pro as highest quality, flash as balanced, and flash-lite as fastest and most economical. The full option list is -m/--model, -t/--tier (marked deprecated in favour of --model), -s/--save-intermediates, -id/--intermediates-dir, -sc/--screenshot-count, -ac/--audio-chunk-minutes, -r/--report, -rd/--reports-dir and -i/--instructions. The --tier values (first, business, economy, budget, experimental, experimentalBudget) map to Gemini 2.0 and 2.5 preview model combinations and are still supported, but the README steers new users to --model. For scripted use, the package installs from npm and exposes the three generator functions with per-stage model keys: screenshotModel, audioModel, mergeModel and transcriptionModel. The README also notes that bunx or bun works faster if you have it, which is a hint about startup cost rather than a functional difference.
Chunking, intermediates and the cleanup contract
Long meetings are handled by splitting audio into chunks of --audio-chunk-minutes length, defaulting to 10, transcribing each, and joining the results. By default intermediates go to a system temporary directory and are removed when processing completes. Passing --save-intermediates keeps them, either in a hidden .offmute_[filename] directory next to the input or in the path given by --intermediates-dir. The README documents the saved layout as screenshots/, audio/, transcription/ and report/ subdirectories. That layout is the most useful diagnostic surface the tool offers: if a transcript reads oddly at a specific point, the per-chunk transcription files let you see which chunk produced it. The cost is disk and, for long recordings, a lot of it, since audio chunks and screenshots are retained. The README also describes incremental output: output files are created early and updated as processing proceeds, so partial results are visible during a long run. That is a reasonable design for a process that can take minutes per meeting, though it means a partially written file is indistinguishable from a finished one unless you watch the process exit.
Where the LLM-only approach breaks: chunk boundaries and speaker identity
The clearest limitation follows from the architecture. Diarization happens inside a model call scoped to one audio chunk. A ten-minute chunk has no knowledge of the nine chunks around it, and the README does not describe any cross-chunk reconciliation of speaker labels. If two people speak in chunk one and the same two speak in chunk two, there is no documented mechanism guaranteeing they receive the same label in both. The description stage passes context forward, and userInstructions can nudge the model, but neither is a deterministic identity system. A second limitation is that the material never mentions word-level or segment-level timestamps. For meeting notes that is fine. For anything that needs to jump to a moment in the recording, or align a transcript with a video player, the output as documented does not support it. Third, everything is a hosted API call. The audio, the screenshots and the derived text all go to Gemini, and the tool has no offline mode. If your recordings cannot leave your infrastructure, this is the wrong tool regardless of output quality. Finally, the project has no releases listed and the README is the only documentation surface described here, so version-to-version behaviour is not pinned by anything you can read in advance.
The realistic alternative: keep the ASR and diarization stages local
The obvious alternative is the conventional pipeline: Whisper or a similar local ASR model for transcription, paired with a speaker diarization library such as pyannote, then optionally an LLM for the summary step. The difference in approach is not quality, it is where the decisions are made. In that pipeline, diarization is a clustering problem solved over the whole recording, so a speaker's identity is computed once against every segment and labels are consistent by construction. Timestamps come out of the ASR stage as a first-class output. Nothing is sent to a third party. The cost is operational: you install and run model weights, you manage GPU or CPU capacity, and you write the glue that turns ASR segments plus diarization segments into a readable transcript. offmute's bet is that a sufficiently capable multimodal model can skip all of that and produce something good enough in one pass, and for internal meeting notes the bet is defensible. It stops being defensible the moment you need reproducible speaker labels, timestamps, or a data path that never leaves your network.
Licence, maintenance and what the --tier deprecation tells you
offmute is Apache-2.0, which permits commercial use, modification and redistribution provided the licence and notices are preserved. That covers the offmute code. It does not cover the Gemini API: your usage is governed by Google's terms and pricing, and the README's model tiers are a direct cost dial, with flash-lite described as the most economical and pro as highest quality. Nothing in the supplied material states a price, a quota or a rate limit, so budget from Google's published pricing rather than from this repository. On maintenance, the deprecation of --tier in favour of --model is the interesting signal. The tier system mapped named bundles to Gemini 2.0 and 2.5 preview models; the replacement exposes the model name directly. That is a smaller surface to keep current when Google retires a model, but it also means the tool's quality now tracks whatever Gemini version you name, and the README's model list will age faster than the code around it. The last push recorded for the repository is April 2026, and no releases are listed, so there is no changelog to consult before upgrading. Treat the model flag as the thing to re-read after any npm update.
Editorial conclusion
Adopt offmute if you already have a Gemini API key, your recordings are ordinary meeting audio or video, and you want a transcript plus a structured report without standing up a local ASR pipeline. Skip it if you need word-level timestamps, deterministic speaker labels across sessions, or any processing that does not leave your machine: every stage here is a prompt to a hosted model. Before committing, run npx offmute --help to confirm the installed version still exposes --model rather than the deprecated --tier, and test one recording at each chunk boundary to see how speaker labels survive the split.
Community notes