+ Older guides say to right-click the tray icon. Since v0.18.1 the tray menu is slimmer:
+ Start on Login lives only in Settings → General, where the settings search also finds it
+ as “autostart”.
+
+
+
Not a systemd Service
diff --git a/web/src/app/custom-dictionary/page.tsx b/web/src/app/custom-dictionary/page.tsx
new file mode 100644
index 0000000..a649e45
--- /dev/null
+++ b/web/src/app/custom-dictionary/page.tsx
@@ -0,0 +1,451 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import {
+ BookOpen,
+ CheckCircle2,
+ ChevronRight,
+ Code,
+ Terminal,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { TerminalBlock } from "@/components/terminal-block";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/custom-dictionary";
+
+const dictionaryFileCommand =
+ "vocalinux --dictionary-file /path/to/dictionary.txt";
+
+const howToSteps = [
+ {
+ name: "Open the Custom Dictionary page",
+ text: "Open Settings from the tray menu and choose Custom Dictionary in the sidebar.",
+ },
+ {
+ name: "Add custom terms",
+ text: "Switch on Use custom terms, type a name or term into the Add a term field and click Add term. Or point Terms file at an existing UTF-8 file.",
+ },
+ {
+ name: "Add transcript corrections",
+ text: "Under Transcript corrections, enter what the engine writes (for example super base) and what you want instead (for example Supabase), then click Add correction.",
+ },
+ {
+ name: "Optionally set a post-processing script",
+ text: "Open the Post-Processing page, enter the path to an executable script in Script Path or click Browse…, and leave the field empty to turn it off.",
+ },
+ {
+ name: "Dictate",
+ text: "Both dictionary files are re-read before each completed segment, so changes apply to your next dictation without a restart.",
+ },
+];
+
+const pipeline = [
+ "The engine transcribes your speech, biased by custom terms on Whisper, whisper.cpp and Faster Whisper.",
+ "Transcript corrections replace known mishearings. This works with every engine.",
+ "Voice commands are interpreted, if Voice Commands is on.",
+ "The post-processing script, if set, rewrites the final text.",
+ "The result is typed into the focused app, or appended to the Dictation Pad.",
+];
+
+const faqs = [
+ {
+ q: "Which engines use custom terms?",
+ a: "Vocabulary bias works with Whisper, whisper.cpp and Faster Whisper. Transcript corrections work with every engine, including VOSK, Parakeet and the Remote API.",
+ },
+ {
+ q: "Are corrections added to the vocabulary bias automatically?",
+ a: "No. Corrections and terms are separate. If you want the engine to lean toward a spelling as well as fix it afterwards, add it as a term too.",
+ },
+ {
+ q: "My correction triggered a voice command. Why?",
+ a: "Corrections run before voice commands. If a replacement is itself a command phrase, it will act as one. Choose replacement text that is not a voice command unless that is what you want.",
+ },
+ {
+ q: "My post-processing script runs but nothing changes. What should I check?",
+ a: "Vocalinux falls back to the original text when the script exits non-zero, cannot be started, or runs longer than 10 seconds. Make sure the file is executable, the path is absolute, and the script writes to standard output. The Vocalinux log records the exit code and standard error.",
+ },
+ {
+ q: "Why did a segment disappear after I added a script?",
+ a: "If the script exits successfully but prints nothing, Vocalinux types nothing for that segment. Make sure every code path writes the text back out.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: "Custom Dictionary, Corrections and Post-Processing | Vocalinux",
+ description:
+ "Teach Vocalinux your jargon: custom terms that bias Whisper, transcript corrections for every engine, and a stdin/stdout post-processing script hook.",
+ path: PAGE_PATH,
+ keywords: [
+ "custom dictionary voice dictation Linux",
+ "Whisper custom vocabulary",
+ "whisper.cpp initial prompt terms",
+ "speech to text corrections",
+ "dictation post-processing script",
+ "Vocalinux custom dictionary",
+ ],
+});
+
+export default function CustomDictionaryPage() {
+ const articleJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "TechArticle",
+ headline:
+ "Custom Dictionary, Transcript Corrections and Post-Processing in Vocalinux",
+ description:
+ "How Vocalinux custom terms, transcript corrections and the post-processing script hook work, with exact file formats and safe example scripts.",
+ dateModified: "2026-11-01",
+ author: {
+ "@type": "Person",
+ name: "Jatin K Malik",
+ url: "https://github.com/jatinkrmalik",
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "Vocalinux",
+ logo: {
+ "@type": "ImageObject",
+ url: absoluteUrl("/vocalinux.png"),
+ },
+ },
+ mainEntityOfPage: absoluteUrl(PAGE_PATH),
+ };
+
+ const howToJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "HowTo",
+ name: "Set up a custom dictionary in Vocalinux",
+ step: howToSteps.map((step, index) => ({
+ "@type": "HowToStep",
+ position: index + 1,
+ name: step.name,
+ text: step.text,
+ })),
+ };
+
+ const faqJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: faqs.map((item) => ({
+ "@type": "Question",
+ name: item.q,
+ acceptedAnswer: { "@type": "Answer", text: item.a },
+ })),
+ };
+
+ return (
+
+
+
+
+
+
+
Added in v0.18.0
+
+ Custom dictionary, corrections and post-processing
+
+
+ Speech models stumble on product names, colleagues' names and
+ project jargon. Vocalinux gives you three tools for that: custom terms
+ that steer Whisper-family engines toward your vocabulary, transcript
+ corrections that fix known mishearings on every engine, and a script
+ hook that can rewrite each result before it is typed.
+
+
+
+
+
+
+
Custom terms
+
+ A plain text list, one term per line, passed to the engine as
+ context so it is more likely to spell those words your way.
+
+
+
+
+
Transcript corrections
+
+ Heard-as and replace-with pairs applied to every finished
+ transcript, whatever the engine.
+
+
+
+
+
Post-processing script
+
+ Any executable that reads text on stdin and writes the replacement
+ on stdout.
+
+
+
+
+
+
Set it up
+
+ {howToSteps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
+
+
+
+ Order of operations
+
+
+ {pipeline.map((item) => (
+
{item}
+ ))}
+
+
+ Because corrections come before voice commands, a correction can stop
+ a mishearing from triggering a destructive command. The flip side:
+ avoid replacements that are themselves command phrases. The command
+ list is on the voice commands page.
+
+
+
+
+
+ Custom terms file
+
+
+ Terms live in ~/.config/vocalinux/dictionary.txt by
+ default: UTF-8, one term per line. Blank lines and lines starting with{" "}
+ # are skipped, and duplicates are ignored regardless of
+ case. The plain line format is deliberate, so accessibility tools and
+ scripts can maintain the same file.
+
+ Bias applies to Whisper, whisper.cpp and Faster Whisper. On
+ whisper.cpp the terms are appended after anything in Advanced →
+ Initial Prompt, so your own prompt is kept.
+
+
+ Up to 200 terms are used by default (
+ dictionary.max_words in config.json), and the combined
+ prompt stops at 2,000 characters.
+
+
+ The editor rejects a single term longer than 200 characters. A file
+ larger than 1 MiB is read only up to its last complete line inside
+ that limit.
+
+
+
Session-only terms file
+
+ To try a different list without touching saved settings, start
+ Vocalinux with:
+
+
+
+ The custom terms controls are disabled for that session. Transcript
+ corrections still apply.
+
+
+
+
+
+ Transcript corrections file
+
+
+ Corrections are stored separately in{" "}
+ ~/.config/vocalinux/custom-dictionary-corrections.json,
+ always in the config directory, whatever terms file you use. The
+ format is versioned:
+
+ heard matches case-insensitively, only as a whole word
+ or phrase.
+
+
+ replacement is inserted with exactly the spelling you
+ typed.
+
+
+ Longer phrases win over shorter ones. For a repeated heard phrase,
+ the first wins.
+
+
Up to 500 corrections, each side up to 500 characters.
+
+ Settings refuses to edit a hand-written file with extra fields or
+ entries it would have to change. Fix the JSON, or edit it only by
+ hand.
+
+
+
+
+
+
+
+ The post-processing script hook
+
+
+ The path is saved as post_processing.script_path (empty
+ means off). For each completed segment, Vocalinux runs the file like
+ this:
+
+
+
+
+ {[
+ [
+ "Invocation",
+ "The path is executed directly, with no arguments and no shell. Use an absolute path to an executable file; ~ is not expanded.",
+ ],
+ [
+ "stdin",
+ "The text of one segment, after corrections and voice commands.",
+ ],
+ [
+ "stdout",
+ "The replacement text, used verbatim. One trailing newline is dropped if the input had none, so echo and print are safe; other newlines are kept.",
+ ],
+ [
+ "Environment",
+ "Inherits Vocalinux's environment. When Vocalinux runs from the AppImage, the bundle's library paths are removed first.",
+ ],
+ ["Timeout", "10 seconds."],
+ [
+ "Failure",
+ "A non-zero exit, a timeout or a script that cannot start means the original text is used. Standard error is logged on a non-zero exit.",
+ ],
+ ["Empty output", "Nothing is typed for that segment."],
+ [
+ "Ordering",
+ "Scripts run one at a time on a dedicated worker, in order, so a slow script does not interrupt dictation. A waiting segment is dropped if you switch to another app before it is typed, unless it is going to the Dictation Pad.",
+ ],
+ ].map(([label, value]) => (
+
+
+ {label}
+
+
{value}
+
+ ))}
+
+
+
+
Example: uppercase everything
+
+ The example from the Vocalinux user guide. Make it executable with{" "}
+ chmod +x before setting the path.
+
+
+ {`#!/bin/bash
+tr '[:lower:]' '[:upper:]'`}
+
+
+ Example: typographic apostrophes in Python
+
+
+ Reads everything from stdin and writes it back without adding a
+ newline. Any exception exits non-zero, so Vocalinux falls back to the
+ original text.
+
+ Before pointing Vocalinux at a script, pipe a sample sentence into it
+ from a terminal and check that it prints the text back. Output is not
+ written to the log, but anything your script does with the text
+ (files, network calls) is up to the script.
+
+
+
+
+
FAQ
+
+ {faqs.map((item) => (
+
+
{item.q}
+
{item.a}
+
+ ))}
+
+
+
+
+
+ Make dictation speak your language
+
+
+ Custom terms help most with a Whisper-family engine; the{" "}
+ engine comparison and{" "}
+ advanced settings pages cover
+ the rest of the tuning. Developers may also like{" "}
+ dictation for code.
+
diff --git a/web/src/app/dictation-pad/page.tsx b/web/src/app/dictation-pad/page.tsx
new file mode 100644
index 0000000..36f039d
--- /dev/null
+++ b/web/src/app/dictation-pad/page.tsx
@@ -0,0 +1,432 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import {
+ CheckCircle2,
+ ChevronRight,
+ Copy,
+ Keyboard,
+ Monitor,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/dictation-pad";
+
+const howToSteps = [
+ {
+ name: "Turn on pad capture",
+ text: "Open Settings from the tray menu, go to the Dictation page and, in the Output group, switch on Dictate into Dictation Pad.",
+ },
+ {
+ name: "Open the pad",
+ text: "Click Open on the Open Dictation Pad row, or choose Dictation Pad from the tray menu. A window titled Vocalinux Dictation Pad appears.",
+ },
+ {
+ name: "Dictate as usual",
+ text: "Use your normal dictation shortcut. Each finished segment is appended to the end of the pad instead of being typed into the focused app.",
+ },
+ {
+ name: "Copy the text out",
+ text: "Click Copy All to put the whole pad on the clipboard (the button briefly reads Copied), or select part of the text and copy it with your keyboard. Paste it into the app you are working in.",
+ },
+ {
+ name: "Clear when done",
+ text: "Click Clear to empty the pad for the next piece of writing.",
+ },
+];
+
+const settingsRows = [
+ {
+ label: "Dictate into Dictation Pad",
+ where: "Settings → Dictation → Output",
+ key: "text_injection.dictate_to_pad",
+ note: "Off by default. On routes every dictated segment into the pad.",
+ },
+ {
+ label: "Open Dictation Pad",
+ where: "Settings → Dictation → Output",
+ key: "(button)",
+ note: "Shows the pad window. The tray menu has the same Dictation Pad entry.",
+ },
+ {
+ label: "Voice Commands",
+ where: "Settings → Dictation → Output",
+ key: "speech_recognition.voice_commands_enabled",
+ note: "Needed if you want spoken editing commands to act on the pad.",
+ },
+ {
+ label: "Copy to Clipboard",
+ where: "Settings → Dictation → Output",
+ key: "text_injection.copy_to_clipboard",
+ note: "Separate option that copies each result to the clipboard. Not required for the pad.",
+ },
+];
+
+const faqs = [
+ {
+ q: "Does the Dictation Pad save my text to disk?",
+ a: "No. The pad keeps its text for the current session only. Closing the window hides it and keeps the text, but quitting Vocalinux discards it. Copy anything you want to keep before you quit.",
+ },
+ {
+ q: "Why does the pad not stay on top of my other windows on Wayland?",
+ a: "Vocalinux asks for the pad to float above other windows and appear on every workspace, which works on X11. Wayland has no always-on-top protocol for normal apps, so the compositor decides. If a covered pad stops drawing, choose Dictation Pad from the tray menu again and Vocalinux recreates the window.",
+ },
+ {
+ q: "Do I still need IBus, ydotool or wtype?",
+ a: "Not for getting text out. Pad capture skips cross-application text injection entirely. You still need a working dictation shortcut, which on Wayland means the built-in listener with access to input devices, or External Activation (Desktop Shortcut) with a desktop shortcut that runs vocalinux --toggle.",
+ },
+ {
+ q: "Can I edit text inside the pad?",
+ a: "Yes. The pad is an ordinary editable text box. You can type, fix words and delete lines by hand, and new dictation is still appended at the end.",
+ },
+ {
+ q: "Do voice commands work in the pad?",
+ a: "Yes, when Voice Commands is on. While pad capture is on, editing commands such as delete that, undo, redo, select all, cut, copy and paste act on the pad and never reach the app that has keyboard focus. Editing commands are English-only.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: "Dictation Pad: Wayland-Safe Voice Typing on Linux | Vocalinux",
+ description:
+ "Dictate into the Vocalinux Dictation Pad, then copy text out by hand. The Wayland-safe path that skips text injection. Setup, settings, limits and FAQ.",
+ path: PAGE_PATH,
+ keywords: [
+ "Wayland voice typing",
+ "Wayland dictation workaround",
+ "Linux dictation pad",
+ "voice typing without text injection",
+ "Vocalinux Dictation Pad",
+ "speech to text window Linux",
+ ],
+});
+
+export default function DictationPadPage() {
+ const articleJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "TechArticle",
+ headline: "Dictation Pad: Wayland-Safe Voice Typing on Linux",
+ description:
+ "How the Vocalinux Dictation Pad receives dictation in its own window so text never depends on cross-application injection, and how to copy it out.",
+ dateModified: "2026-11-01",
+ author: {
+ "@type": "Person",
+ name: "Jatin K Malik",
+ url: "https://github.com/jatinkrmalik",
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "Vocalinux",
+ logo: {
+ "@type": "ImageObject",
+ url: absoluteUrl("/vocalinux.png"),
+ },
+ },
+ mainEntityOfPage: absoluteUrl(PAGE_PATH),
+ };
+
+ const howToJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "HowTo",
+ name: "Dictate into the Vocalinux Dictation Pad",
+ step: howToSteps.map((step, index) => ({
+ "@type": "HowToStep",
+ position: index + 1,
+ name: step.name,
+ text: step.text,
+ })),
+ };
+
+ const faqJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: faqs.map((item) => ({
+ "@type": "Question",
+ name: item.q,
+ acceptedAnswer: { "@type": "Answer", text: item.a },
+ })),
+ };
+
+ return (
+
+
+
+
+
+
+
Added in v0.18.0
+
+ Dictation Pad: voice typing that skips text injection
+
+
+ The Dictation Pad is a small Vocalinux window with an editable text
+ box. When pad capture is on, everything you dictate lands there
+ instead of being typed into another app. You copy the text out
+ yourself. Because nothing is injected into other windows, the
+ compositor rules that make keystroke injection fragile on Wayland
+ never come into play.
+
+
+
+
+
+
+
Who it is for
+
+ Wayland users whose compositor blocks or garbles injected text, and
+ anyone who wants to read and fix a draft before it reaches the real
+ app.
+
+
+
+
+
How text gets out
+
+ Copy All puts the whole pad on the clipboard. You can also select
+ part of the text and copy it like any other text box.
+
+
+
+
+
What stays the same
+
+ Your shortcut, engine, transcript corrections and post-processing
+ script all work as before. Only the destination of the text changes.
+
+
+
+
+
+
+ Why the Dictation Pad exists
+
+
+ On X11, Vocalinux types into the focused window with xdotool. Wayland
+ deliberately stops apps from sending keystrokes to each other, so
+ Vocalinux has to go through IBus, the RemoteDesktop portal, ydotool or
+ wtype, and which of those works depends on your desktop. Most setups
+ are covered (see the Wayland guide), but
+ some compositor and app combinations still drop or mangle text.
+
+
+ The pad is the explicit fallback for those cases. Dictation is written
+ straight into a Vocalinux window, so there is no injection step left
+ to fail. Text bound for the pad also lands there even if you switched
+ to another app while it was being transcribed.
+
+
+
+
+
+ How to use it
+
+
+ {howToSteps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
+ In v0.18.4 the switch moved to Settings → Dictation, next to the Open
+ Dictation Pad button. On older 0.18 releases, update first; the{" "}
+ changelog lists what changed in each
+ patch.
+
+
+
+
+
+ Settings it touches
+
+
+
+
+
+
Setting
+
Where
+
config.json key
+
What it does
+
+
+
+ {settingsRows.map((row) => (
+
+
{row.label}
+
+ {row.where}
+
+
+ {row.key}
+
+
{row.note}
+
+ ))}
+
+
+
+
+ The switch is stored in ~/.config/vocalinux/config.json.
+ Flipping it in Settings applies to the next dictated segment without a
+ restart. The saved value looks like this:
+
+ The text box is editable, so you can fix a word with the keyboard
+ between dictations. With Voice Commands on, spoken editing commands
+ target the pad while capture is on: delete that removes the
+ last dictated segment, undo and redo step through
+ the pad's own history, and select all,{" "}
+ select line, select word, select paragraph,{" "}
+ cut, copy and paste work on the pad's
+ text. They never reach the app that happens to have focus. The full
+ command list is on the{" "}
+ voice commands page.
+
+
+ Recent snippets as a second safety net
+
+
+ The tray's Recent Snippets menu keeps your last dictations (10 by
+ default) in memory. Clicking one copies it to the clipboard, which
+ helps when a single segment went to the wrong place.
+
+
+
+
+
+ Limitations and gotchas
+
+
+
+
+
+
+ Copying out is manual.
+ {" "}
+ The pad never pastes for you. That extra step is the trade for not
+ depending on injection.
+
+
+
+
+
+ Session only. Text
+ survives closing the window, not quitting the app.
+
+
+
+
+
+
+ No always-on-top on Wayland.
+ {" "}
+ A covered, idle pad can be shelved by the compositor. Reopen it
+ from the tray. v0.18.1 fixed the pad ghosting away, and v0.18.5
+ stopped it travelling between workspaces with Settings.
+
+
+
+
+
+
+ The shortcut still has to fire.
+ {" "}
+ The pad solves text output, not hotkeys. On Wayland the built-in
+ listener needs access to /dev/input, or you can turn
+ on External Activation (Desktop Shortcut) and bind{" "}
+ vocalinux --toggle to a desktop shortcut (toggle
+ only, no push-to-talk). See{" "}
+ global shortcuts and{" "}
+ Hyprland and sway.
+
+
+
+
+
+
+
FAQ
+
+ {faqs.map((item) => (
+
+
{item.q}
+
{item.a}
+
+ ))}
+
+
+
+
+
+ Try the Dictation Pad
+
+
+ Install Vocalinux, open Settings → Dictation and switch on Dictate
+ into Dictation Pad. If injection already works on your desktop, the{" "}
+ troubleshooting guide covers the
+ other paths.
+
+
+
+ Install Vocalinux
+
+
+
+ Wayland guide
+
+
+
+
+ );
+}
diff --git a/web/src/app/faster-whisper/page.tsx b/web/src/app/faster-whisper/page.tsx
new file mode 100644
index 0000000..e6cbe0a
--- /dev/null
+++ b/web/src/app/faster-whisper/page.tsx
@@ -0,0 +1,617 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import {
+ CheckCircle2,
+ ChevronRight,
+ Cpu,
+ Download,
+ Lock,
+ XCircle,
+ Zap,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const installCommands = [
+ "curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh",
+ "bash /tmp/vl.sh --auto --engine=faster_whisper",
+];
+
+const setupSteps = [
+ {
+ title: "Install the Faster Whisper engine",
+ text: "Faster Whisper is an optional extra. Run the installer and choose Faster-Whisper in the engine menu, or pass --engine=faster_whisper. If the faster-whisper package fails to install, the installer falls back to whisper.cpp and says so.",
+ },
+ {
+ title: "Open Settings → Speech Model",
+ text: "Open Settings and go to Speech Model. Set your Language, then expand Advanced to see the Engine and Model size pickers.",
+ },
+ {
+ title: "Pick Faster Whisper and a size",
+ text: "Set Engine to Faster Whisper and choose a Model size. Settings suggests one for your hardware. Picking only selects the model; nothing downloads yet.",
+ },
+ {
+ title: "Confirm the download",
+ text: "If the model is not on disk, click Download and confirm. Every file is checked against a pinned checksum before the engine loads it.",
+ },
+ {
+ title: "Dictate",
+ text: "Hold your dictation shortcut (Right Alt by default on new installs), speak, and release. Text is typed into the focused app.",
+ },
+];
+
+const sizes = [
+ {
+ size: "Tiny",
+ ids: "tiny, tiny.en",
+ params: "39M",
+ desc: "Fastest, lowest accuracy",
+ },
+ {
+ size: "Base",
+ ids: "base, base.en",
+ params: "74M",
+ desc: "Fast, good for basic use",
+ },
+ {
+ size: "Small",
+ ids: "small, small.en",
+ params: "244M",
+ desc: "Balanced speed and accuracy",
+ },
+ {
+ size: "Medium",
+ ids: "medium, medium.en",
+ params: "769M",
+ desc: "High accuracy, slower",
+ },
+ {
+ size: "Large v3",
+ ids: "large-v3",
+ params: "1550M",
+ desc: "Highest accuracy, slower",
+ },
+];
+
+const packages = [
+ {
+ name: "install.sh / source install",
+ included: true,
+ note: "Menu option or --engine=faster_whisper",
+ },
+ {
+ name: "AUR (vocalinux)",
+ included: true,
+ note: "Listed as an optional dependency: python-faster-whisper",
+ },
+ {
+ name: "AppImage (and AUR vocalinux-bin)",
+ included: false,
+ note: "Ships whisper.cpp only",
+ },
+ { name: "Flatpak", included: false, note: "Ships whisper.cpp only" },
+ { name: "Snap", included: false, note: "Ships whisper.cpp only" },
+ {
+ name: ".deb / .rpm",
+ included: false,
+ note: "Thin packages; whisper.cpp on CPU",
+ },
+ {
+ name: "Nix flake",
+ included: false,
+ note: "faster_whisper extra is not packaged",
+ },
+];
+
+const choices = [
+ {
+ engine: "whisper.cpp",
+ pick: "The default. Ships in every package, uses a Vulkan GPU on AMD, Intel and NVIDIA, and offers quantized and English-only variants. Start here.",
+ },
+ {
+ engine: "Faster Whisper",
+ pick: "You installed with install.sh, from source or from the AUR and want a Whisper model on a CPU-only machine, running through CTranslate2 instead of whisper.cpp.",
+ },
+ {
+ engine: "Whisper (OpenAI)",
+ pick: "You already work with PyTorch and NVIDIA CUDA and accept a much larger install.",
+ },
+ {
+ engine: "VOSK",
+ pick: "Older or low-RAM machines. Small models are about 40 MB, with one model per language.",
+ },
+];
+
+const faqs = [
+ {
+ question: "Is Faster Whisper the same model as Whisper?",
+ answer:
+ "It runs the same Whisper model family converted to the CTranslate2 format. Vocalinux downloads those conversions from pinned Hugging Face revisions. The sizes and English-only variants match the Whisper names you already know.",
+ },
+ {
+ question: "Does Faster Whisper use my GPU?",
+ answer:
+ "On a typical install it runs on the CPU with int8. Vocalinux switches to CUDA with float16 only when PyTorch is installed and reports a CUDA GPU. The installer's Faster Whisper option does not install CUDA. For Vulkan GPU acceleration on AMD, Intel or NVIDIA, use whisper.cpp.",
+ },
+ {
+ question: "Can I change the compute type?",
+ answer:
+ "There is no setting for it. Vocalinux picks int8 on the CPU and float16 on CUDA automatically.",
+ },
+ {
+ question: "Does audio leave my machine?",
+ answer:
+ "No. Faster Whisper is a local engine. After the model download, recognition runs on your machine. Only the Remote API engine and VocaGateway send audio to an endpoint you configure.",
+ },
+ {
+ question: "Why is Faster Whisper not in my Engine list?",
+ answer:
+ "Settings lists only engines whose libraries are installed. The AppImage, Flatpak, Snap, .deb and .rpm packages do not include faster-whisper. Re-run the installer with --engine=faster_whisper, or on Arch install python-faster-whisper.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: "Faster Whisper on Linux: Offline Voice Typing | Vocalinux",
+ description:
+ "Use Faster Whisper (CTranslate2, int8 on CPU) for offline voice typing on Linux with Vocalinux: install, model sizes, GPU and compute options, and fixes.",
+ path: "/faster-whisper",
+ keywords: [
+ "Faster Whisper Linux",
+ "faster-whisper dictation",
+ "CTranslate2 Whisper Linux",
+ "Faster Whisper vs whisper.cpp",
+ "offline Whisper CPU int8",
+ "Linux voice typing Whisper",
+ "faster-whisper model sizes",
+ ],
+});
+
+export default function FasterWhisperPage() {
+ const jsonLd = [
+ {
+ "@context": "https://schema.org",
+ "@type": "TechArticle",
+ headline: "Faster Whisper on Linux",
+ description:
+ "How to install and use the Faster Whisper speech engine in Vocalinux on Linux, including model sizes, compute options, package availability and troubleshooting.",
+ dateModified: "2026-11-01",
+ author: {
+ "@type": "Person",
+ name: "Jatin K Malik",
+ url: "https://github.com/jatinkrmalik",
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "Vocalinux",
+ logo: {
+ "@type": "ImageObject",
+ url: absoluteUrl("/vocalinux.png"),
+ },
+ },
+ mainEntityOfPage: absoluteUrl("/faster-whisper"),
+ },
+ {
+ "@context": "https://schema.org",
+ "@type": "HowTo",
+ name: "Set up the Faster Whisper engine in Vocalinux",
+ step: setupSteps.map((step, index) => ({
+ "@type": "HowToStep",
+ position: index + 1,
+ name: step.title,
+ text: step.text,
+ })),
+ },
+ {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: faqs.map((faq) => ({
+ "@type": "Question",
+ name: faq.question,
+ acceptedAnswer: { "@type": "Answer", text: faq.answer },
+ })),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+ Added in v0.17.0
+
+
+ Faster Whisper on Linux
+
+
+ Faster Whisper is an optional local speech engine in Vocalinux. It
+ runs Whisper models through CTranslate2 with int8 quantization on your
+ CPU and types what you say into the focused app. It is for people who
+ want Whisper models and the full language catalog on a machine without
+ a GPU, using a different runtime from the default whisper.cpp.
+
+
+
+
+
+
+
CPU first
+
+ int8 on the CPU by default. No PyTorch needed for the CPU path.
+
+
+
+
+
On-device
+
+ After the one-time model download, recognition runs locally. Audio
+ stays on your machine.
+
+
+
+
+
Pinned models
+
+ Models load only from checksum-verified local files, never from an
+ unpinned upstream revision.
+
+
+
+
+
+
+ What is Faster Whisper?
+
+
+
+
+ faster-whisper
+ {" "}
+ is a reimplementation of OpenAI's Whisper on top of
+ CTranslate2, an inference engine for transformer models. It runs
+ converted Whisper models and supports reduced precision such as
+ int8, which lowers memory use on the CPU.
+
+
+ Vocalinux added Faster Whisper as an optional engine in v0.17.0,
+ together with Parakeet. whisper.cpp remains the default. Vocalinux
+ stores Faster Whisper models under{" "}
+
+ ~/.local/share/vocalinux/models/
+ {" "}
+ and checks each file against a pinned checksum before loading it.
+
+ To start once with Faster Whisper from a terminal, run{" "}
+
+ vocalinux --engine faster_whisper
+
+ .
+
+
+
+
+
+ Model sizes
+
+
+ The Model size picker offers these sizes. The .en{" "}
+ variants are English only; if your language is English, Settings
+ suggests the .en variant of the recommended size. There
+ is no large-v3.en.
+
+
+
+
+
+
Size
+
Model IDs
+
Parameters
+
Description
+
+
+
+ {sizes.map((row) => (
+
+
{row.size}
+
{row.ids}
+
+ {row.params}
+
+
{row.desc}
+
+ ))}
+
+
+
+
+ Which size Settings suggests
+
+
+
CPU only, under 16 GB RAM: Tiny
+
CPU only, 16 GB RAM or more: Base
+
CUDA GPU detected, under 8 GB RAM: Base
+
CUDA GPU detected, 8 GB RAM or more: Small
+
+
+ The suggestion never changes your model or starts a download. If
+ dictation feels slow, go one size down; if it makes too many mistakes
+ and speed is fine, go one size up. The{" "}
+
+ Whisper model guide
+ {" "}
+ explains the size trade-offs in more depth.
+
+
+
+
+
+ Compute options and GPUs
+
+
+
+ Vocalinux chooses the device and precision for you; there is no
+ compute-type setting. On the CPU it loads the model with{" "}
+ int8. If PyTorch is
+ installed and reports a CUDA GPU, it loads the model on CUDA with{" "}
+ float16 instead.
+
+
+ The installer's Faster Whisper option is the CPU CTranslate2
+ path and does not install the CUDA toolkit. If you want GPU
+ dictation on AMD, Intel or NVIDIA, whisper.cpp with Vulkan is the
+ supported route; see{" "}
+
+ GPU acceleration
+
+ .
+
+
+
+
+
+
Languages
+
+ Faster Whisper uses the same language catalog as the other Whisper
+ engines, including Auto-detect. It also supports{" "}
+ Follow keyboard layout, which switches the dictation
+ language with your active layout, and custom dictionary terms that
+ bias recognition toward names and jargon. See{" "}
+
+ languages by engine
+
+ ,{" "}
+
+ multilingual dictation
+ {" "}
+ and{" "}
+
+ custom dictionary
+
+ .
+
+ Faster Whisper vs whisper.cpp, Whisper and VOSK
+
+
+ All four are local engines. Choose by hardware and by how you
+ installed Vocalinux. For the full table, see the{" "}
+
+ engine comparison
+ {" "}
+ or{" "}
+
+ Parakeet
+ {" "}
+ for the other CPU engine added in v0.17.0.
+
+
+
+
+
+
Engine
+
Pick it when
+
+
+
+ {choices.map((row) => (
+
+
+ {row.engine}
+
+
{row.pick}
+
+ ))}
+
+
+
+
+
+
+
+ Troubleshooting
+
+
+
+
+ The installer finished, but whisper.cpp is selected
+
+
+ If faster-whisper fails to install or import, the installer falls
+ back to whisper.cpp. Read the installer output, then re-run it
+ with{" "}
+
+ --engine=faster_whisper
+
+ .
+
+
+
+
+ Another language is not recognized
+
+
+ The .en models are English only. Pick the size
+ without .en, or set Language to Auto-detect with a
+ multilingual model.
+
+
+
+
+ Dictation feels slow
+
+
+ Go one size down. On CPU-only machines, Tiny or Base is the usual
+ starting point. Larger sizes take longer per sentence on a CPU.
+
+
+
+
+ Anything else
+
+
+ Run{" "}
+ vocalinux --debug{" "}
+ and check{" "}
+
+ ~/.local/share/vocalinux/vocalinux.log
+
+ , then see the{" "}
+
+ troubleshooting guide
+
+ .
+
+
+
+
+
+
+
+ Faster Whisper FAQ
+
+
+ {faqs.map((faq) => (
+
+
{faq.question}
+
{faq.answer}
+
+ ))}
+
+
+
+
+
+ Try Faster Whisper
+
+
+ Install Vocalinux with the Faster Whisper engine, or keep whisper.cpp
+ and switch later from Settings → Speech Model.
+
+
+
+ );
+}
diff --git a/web/src/app/flatpak/page.tsx b/web/src/app/flatpak/page.tsx
new file mode 100644
index 0000000..1ffbb9e
--- /dev/null
+++ b/web/src/app/flatpak/page.tsx
@@ -0,0 +1,482 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import {
+ AlertTriangle,
+ CheckCircle2,
+ ChevronRight,
+ CircleHelp,
+ Download,
+ Keyboard,
+ RefreshCw,
+ ShieldCheck,
+ XCircle,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/flatpak";
+const PAGE_TITLE = "Install Vocalinux via Flatpak (VocaHQ Remote) | Vocalinux";
+const PAGE_DESCRIPTION =
+ "Install Vocalinux from the signed VocaHQ Flatpak remote (not Flathub): exact commands, updates, sandbox and Wayland notes, what is included, and uninstall.";
+
+const FLATPAKREF_COMMAND =
+ "flatpak install https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref";
+
+const MANUAL_COMMANDS = `flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
+flatpak remote-add --if-not-exists vocahq https://vocahq.github.io/vocalinux-flatpak/vocahq.flatpakrepo
+flatpak install vocahq com.vocalinux.Vocalinux`;
+
+const DBUS_TOGGLE_COMMAND =
+ "gdbus call --session --dest com.vocalinux.Vocalinux --object-path /com/vocalinux/Vocalinux --method com.vocalinux.Vocalinux.Toggle";
+
+const OVERRIDE_COMMAND =
+ "flatpak override --user --nodevice=all --device=dri com.vocalinux.Vocalinux";
+
+const RUN_COMMAND = "flatpak run com.vocalinux.Vocalinux";
+
+const installSteps = [
+ {
+ name: "Install from the VocaHQ remote",
+ text: `Run ${FLATPAKREF_COMMAND}. The .flatpakref adds the vocahq remote and resolves the GNOME runtime (org.gnome.Platform//50) through Flathub, so no separate Flathub setup is needed.`,
+ },
+ {
+ name: "Or add the remotes by hand",
+ text: "If you prefer explicit remotes, add flathub, add vocahq, then install com.vocalinux.Vocalinux from vocahq with the three commands below.",
+ },
+ {
+ name: "Start Vocalinux",
+ text: `Launch Vocalinux from your app menu or run ${RUN_COMMAND}.`,
+ },
+ {
+ name: "Download a speech model",
+ text: "Pick a model in Settings → Speech Model. Since v0.18.4 Vocalinux shows the download size and asks before downloading anything.",
+ },
+];
+
+const faqs = [
+ {
+ question: "Is Vocalinux on Flathub?",
+ answer:
+ "No. The Flathub submission was closed on policy grounds in July 2026 and the project is not re-submitting. The app comes from the self-hosted vocahq remote; only the GNOME runtime is pulled from Flathub.",
+ },
+ {
+ question: "Does the Flatpak update itself?",
+ answer:
+ "Flatpak updates it, not the app. Run flatpak update (or let your software center do it) and each stable release arrives as an incremental delta. Prerelease tags never reach the remote, and GitHub Release .flatpak bundles do not auto-update.",
+ },
+ {
+ question: "Can I use Parakeet or Faster Whisper in the Flatpak?",
+ answer:
+ "No. The Flatpak ships whisper.cpp only. Faster Whisper, Parakeet, OpenAI Whisper and VOSK are available through the install script or a source install.",
+ },
+ {
+ question: "My push-to-talk key does nothing on Wayland. Why?",
+ answer:
+ "On Wayland the built-in hotkey reads /dev/input. The manifest grants --device=all by default; if you removed device access with flatpak override, grant --device=input (Flatpak 1.15.6 or newer) or --device=all again, or enable External Activation and bind a desktop shortcut to the D-Bus Toggle command. Desktop shortcuts only toggle.",
+ },
+ {
+ question: "Can I start a local VocaGateway from the Flatpak?",
+ answer:
+ "No. The sandbox cannot reach the host podman or docker socket. Start VocaGateway on the host yourself and point Settings → Remote Server at it, or use the installer or AppImage. Either way, audio leaves Vocalinux for the endpoint you configured, and leaves the machine if that endpoint is elsewhere. VocaGateway is not on-device, so keep it on localhost, a trusted LAN, an encrypted private network such as Tailscale, or HTTPS.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: PAGE_TITLE,
+ description: PAGE_DESCRIPTION,
+ path: PAGE_PATH,
+ keywords: [
+ "vocalinux flatpak",
+ "linux voice dictation flatpak",
+ "flatpak speech to text",
+ "vocahq flatpak remote",
+ "com.vocalinux.Vocalinux",
+ "flatpak dictation wayland",
+ ],
+});
+
+function Commands({ children }: { children: string }) {
+ return (
+
+ Vocalinux publishes a signed, self-hosted Flatpak remote called{" "}
+ vocahq on every stable release. It is{" "}
+ not on Flathub: the app comes from VocaHQ, and only
+ the GNOME runtime comes from Flathub. Install once, and{" "}
+ flatpak update picks up each new release.
+
+
+
+
+
+ Who the Flatpak is for
+
+
+ Pick the Flatpak if your distribution already uses Flatpak, if you run
+ an immutable or non-standard layout, or if you want updates handled by
+ the same tool as your other desktop apps. It runs on x86_64 and
+ aarch64, and it ships the default whisper.cpp engine with Vulkan GPU
+ support.
+
+
+ Pick the install script instead if you
+ want Faster Whisper, Parakeet, OpenAI Whisper or VOSK, or the local
+ VocaGateway launcher. Those are not in the Flatpak.
+
+
+
+
+
+
+ Install from the VocaHQ remote
+
+
+
+
1. {installSteps[0].name}
+
+ One command. The .flatpakref adds the{" "}
+ vocahq remote and resolves the GNOME runtime (
+ org.gnome.Platform//50) through Flathub, so no
+ separate Flathub setup is needed.
+
+ {FLATPAKREF_COMMAND}
+
+
+
2. {installSteps[1].name}
+
+ If you prefer explicit remotes, add flathub, add vocahq, then
+ install com.vocalinux.Vocalinux from vocahq:
+
+ {MANUAL_COMMANDS}
+
+
+
3. {installSteps[2].name}
+
+ Launch Vocalinux from your app menu, or run:
+
+ {RUN_COMMAND}
+
+
+
4. {installSteps[3].name}
+
+ Pick a model in Settings → Speech Model. Since v0.18.4 Vocalinux
+ shows the download size and asks before downloading anything.
+ After that one download, the whisper.cpp engine transcribes on
+ your machine. Then hold Right Alt and speak.
+
+
+
+
+
+
+
+
+ Updates
+
+
+ Every stable v* tag is imported into the remote, signed
+ with the VocaHQ GPG key and published with static deltas, so an update
+ downloads only what changed:
+
+ flatpak update
+
+
+ Alpha, beta and rc tags never reach the remote. The only Flatpak for
+ a prerelease is the .flatpak bundle on GitHub Releases.
+
+
+ Release bundles (
+ Vocalinux-<version>-x86_64.flatpak or{" "}
+ -aarch64.flatpak) do not auto-update. Switch to the
+ remote if you installed one.
+
+
+ The in-app update checker only tells you a release exists; it never
+ installs anything.
+
+
+
+ If an update fails signature verification
+
+
+ After a signing-key rotation, existing installs must import the new
+ public key. Re-running remote-add does not replace it.
+ The documented fix:
+
+ The sandbox cannot reach the host podman or docker socket
+
+
+
+
+
+
+ Config and models live under{" "}
+ ~/.var/app/com.vocalinux.Vocalinux/, not{" "}
+ ~/.config/vocalinux.
+
+
+
+
+
+
+ Sandbox permissions and Wayland
+
+
+ The manifest asks for the microphone (--socket=pulseaudio
+ ), the GPU (--device=dri), network for model downloads,
+ the Wayland and X11 sockets, IBus and the tray watcher on D-Bus, and{" "}
+ --device=all for keyboard devices and{" "}
+ /dev/uinput. Flatpak has no narrower flag for uinput.
+
+
Hotkeys
+
+
+ Native X11: since v0.18.4 the Flatpak uses the X11 listener, so the
+ hotkey needs no device access. GNOME on X11 is the exception and
+ still needs /dev/input.
+
+
+ Wayland: the built-in hotkey reads /dev/input. If you
+ tighten permissions, grant --device=input on Flatpak
+ 1.15.6 or newer, or --device=all on older versions.
+
+
+ No device access at all: enable Settings → Shortcuts → External
+ Activation (Desktop Shortcut) and bind a desktop shortcut to the
+ D-Bus Toggle command below. Desktop shortcuts toggle dictation; they
+ cannot do push-to-talk.
+
+
+ {DBUS_TOGGLE_COMMAND}
+
+ To drop device access but keep the GPU for whisper.cpp:
+
+ {OVERRIDE_COMMAND}
+
Typing text
+
+ Text injection prefers the RemoteDesktop portal, which asks for
+ permission once and handles non-US layouts. When the portal is
+ unavailable it falls back to wl-copy plus ydotool paste,
+ then xdotool for X11 and XWayland clients. Without{" "}
+ /dev/uinput the ydotool fallback is gone, but the portal
+ and xdotool remain. On KDE Plasma Wayland, set System Settings →
+ Keyboard → Virtual Keyboard to “IBus Wayland”. On GNOME,
+ the tray icon needs the AppIndicator extension. For more, see{" "}
+ Wayland voice typing and{" "}
+ global shortcuts via D-Bus.
+
+
+
+
+
+
+ Uninstall
+
+
+ The uninstall.sh script is for script and source
+ installs. For the Flatpak, use Flatpak itself. Remove the app:
+
+ flatpak uninstall com.vocalinux.Vocalinux
+
+ Add --delete-data to also remove settings and downloaded
+ models under ~/.var/app/com.vocalinux.Vocalinux/. If no
+ other app uses the remote, drop it too:
+
+ flatpak remote-delete vocahq
+
+
+
+
+
+ Flatpak FAQ
+
+
+ {faqs.map((faq) => (
+
+
{faq.question}
+
{faq.answer}
+
+ ))}
+
+
+
+
+ More fixes for hotkeys, audio and text injection are on the{" "}
+ troubleshooting page. To keep
+ the whole stack local, see the{" "}
+ GPU acceleration guide and{" "}
+ Silero VAD.
+
+
+
+
+
+
+
+ Need a different package?
+
+
+ The install script covers every engine, the AUR and Nix flake fit Arch
+ and NixOS, and the .deb and .rpm suit Debian, Ubuntu and Fedora.
+
+
+
+ All install options
+
+
+
+ Nix flake and NixOS module
+
+
+ Local VocaGateway
+
+
+
+
+ );
+}
diff --git a/web/src/app/global-shortcuts/page.tsx b/web/src/app/global-shortcuts/page.tsx
new file mode 100644
index 0000000..2f04c50
--- /dev/null
+++ b/web/src/app/global-shortcuts/page.tsx
@@ -0,0 +1,460 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import type { ReactNode } from "react";
+import {
+ AlertTriangle,
+ ChevronRight,
+ Command,
+ Keyboard,
+ ShieldCheck,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { TerminalBlock } from "@/components/terminal-block";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/global-shortcuts";
+const PAGE_TITLE = "Global Shortcuts for Voice Dictation via D-Bus";
+const PAGE_DESCRIPTION =
+ "Start and stop Vocalinux dictation from GNOME, KDE, sway or Hyprland shortcuts and scripts using its D-Bus service and the --toggle, --start and --stop flags.";
+
+const GDBUS_TOGGLE =
+ "gdbus call --session --dest com.vocalinux.Vocalinux --object-path /com/vocalinux/Vocalinux --method com.vocalinux.Vocalinux.Toggle";
+
+const dbusRows = [
+ { item: "Bus", value: "Session bus" },
+ { item: "Bus name", value: "com.vocalinux.Vocalinux" },
+ { item: "Object path", value: "/com/vocalinux/Vocalinux" },
+ { item: "Interface", value: "com.vocalinux.Vocalinux" },
+ { item: "Methods", value: "Toggle, Start, Stop (no arguments)" },
+];
+
+const commandRows = [
+ {
+ cli: "vocalinux --toggle",
+ method: "Toggle",
+ effect: "Start if idle, stop if active",
+ },
+ {
+ cli: "vocalinux --start",
+ method: "Start",
+ effect: "Start voice typing; does nothing if already running",
+ },
+ {
+ cli: "vocalinux --stop",
+ method: "Stop",
+ effect: "Stop voice typing; does nothing if already idle",
+ },
+];
+
+const steps = [
+ {
+ name: "Turn on External Activation",
+ text: "Open Settings → Shortcuts (the Keyboard Shortcuts group on the Dictation page) and turn on External Activation (Desktop Shortcut). It applies immediately and the built-in key listener stops.",
+ },
+ {
+ name: "Pick the command",
+ text: "On a native install, use vocalinux --toggle. On Flatpak, use the gdbus call shown below, because vocalinux --toggle is not on the host PATH.",
+ },
+ {
+ name: "Bind it in your desktop",
+ text: "Create a custom shortcut in GNOME or KDE, or a bind line in your sway or Hyprland config, that runs that command.",
+ },
+ {
+ name: "Test it",
+ text: "Focus a text field, press the shortcut, speak, then press it again to stop.",
+ },
+];
+
+const faqs = [
+ {
+ question: "Can a desktop shortcut do push-to-talk?",
+ answer:
+ "No. A desktop shortcut only sends the key press and never the release, so it can toggle dictation but cannot hold-to-talk. Use the built-in listener if you need push-to-talk.",
+ },
+ {
+ question: "Will vocalinux --toggle launch Vocalinux if it is not running?",
+ answer:
+ "No. It forwards the command to a running instance over D-Bus and exits. If no instance answers, it logs that it could not reach a running Vocalinux and exits with status 1.",
+ },
+ {
+ question: "Do I still need the input group?",
+ answer:
+ "Not for starting and stopping dictation. With External Activation on, Vocalinux does not read /dev/input for the hotkey. Text injection is unaffected and keeps its own requirements.",
+ },
+ {
+ question: "What happens if the D-Bus service cannot start?",
+ answer:
+ "Vocalinux shows an External activation unavailable notification and falls back to the internal keyboard shortcut, so you are never left without a way to start or stop dictation.",
+ },
+ {
+ question: "Do per-language shortcuts work with External Activation?",
+ answer:
+ "No. Per-language shortcuts belong to the built-in listener, and the whole listener family stops when External Activation is on. Turn External Activation off to use them again.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: `${PAGE_TITLE} | Vocalinux`,
+ description: PAGE_DESCRIPTION,
+ path: PAGE_PATH,
+ keywords: [
+ "linux dictation global shortcut",
+ "vocalinux d-bus",
+ "vocalinux --toggle",
+ "sway voice typing keybind",
+ "hyprland dictation keybind",
+ "kde global shortcut dictation",
+ "gnome custom shortcut voice typing",
+ ],
+});
+
+function InlineCode({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+function ConfigBlock({ label, children }: { label: string; children: string }) {
+ return (
+
+ {label}
+
+ Start dictation from desktop shortcuts and scripts
+
+
+ Vocalinux can hand hotkey duty to your desktop. A running instance
+ answers on the session D-Bus, so a GNOME or KDE custom shortcut, a
+ sway or Hyprland bind, or any script can start and stop voice typing
+ without Vocalinux reading your keyboard.
+
+
+
+
+
+
+
No /dev/input access
+
+ On Wayland the built-in listener reads{" "}
+ /dev/input, which needs the{" "}
+ input group. A compositor shortcut avoids
+ that entirely.
+
+
+
+
+
Toggle, not push-to-talk
+
+ Your desktop sends the key press only. Press once to start, press
+ again to stop.
+
+
+
+
+
Scriptable
+
+ Separate Start and Stop methods let scripts set a known state
+ instead of flipping it.
+
+
+
+
+
+
+ The D-Bus interface
+
+
+ These names come from dbus_service.py in the
+ Vocalinux source.
+
+
+
+
+ {dbusRows.map((row) => (
+
+
+ {row.item}
+
+
+ {row.value}
+
+
+ ))}
+
+
+
+
CLI commands
+
+
+
+
+
+ Command
+
+
+ D-Bus method
+
+
+ Effect
+
+
+
+
+ {commandRows.map((row) => (
+
+
{row.cli}
+
+ {row.method}
+
+
{row.effect}
+
+ ))}
+
+
+
+
Calling D-Bus directly
+
+ The gdbus form is what the Flatpak
+ documentation recommends: it is immediate and does not start another
+ sandbox on every press. Swap Toggle for{" "}
+ Start or Stop as
+ needed.
+
+
+
+
+
+
Set it up
+
+ {steps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
+ The same switch is stored as{" "}
+ shortcuts.disable_internal_hotkey in{" "}
+ ~/.config/vocalinux/config.json. Quit
+ Vocalinux before editing that file by hand, or the running app can
+ overwrite your change.
+
+
+
+
+
+ Binding the shortcut on each desktop
+
+
+ The key combination is your choice; the examples use Super+Shift+D.
+ Only the command matters to Vocalinux. Menu names and config syntax
+ belong to each desktop and can differ between versions.
+
+
GNOME
+
+ In Settings → Keyboard, open the custom shortcuts list, add a
+ shortcut, enter vocalinux --toggle as the
+ command and record your key combination.
+
+
KDE Plasma
+
+ Open System Settings → Keyboard → Shortcuts → Add New → Command or
+ Script (older Plasma: System Settings → Shortcuts → Custom Shortcuts →
+ Edit → New → Global Shortcut → Command/URL) and bind your keys to{" "}
+ vocalinux --toggle.
+
+ If your compositor cannot find vocalinux, its
+ PATH may not include ~/.local/bin. Use the
+ full path{" "}
+ ~/.local/share/vocalinux/venv/bin/vocalinux{" "}
+ for an installer setup, or the gdbus command
+ above. More on tiling compositors in{" "}
+
+ voice typing on Hyprland and sway
+
+ .
+
+
+
+
+
+
+ Limitations and gotchas
+
+
+
+ Toggle only. Push-to-talk needs the key release, which a desktop
+ shortcut never delivers.
+
+
+ The command needs a session bus. From SSH or a systemd unit without{" "}
+ DBUS_SESSION_BUS_ADDRESS, it fails with a
+ non-zero exit.
+
+
+ While a transcription is still processing, Toggle cancels it, the
+ same as pressing the recording hotkey again (since v0.18.4).
+
+
+ On Flatpak,{" "}
+
+ flatpak run com.vocalinux.Vocalinux --toggle
+ {" "}
+ also works but starts a sandbox on each press; prefer{" "}
+ gdbus.
+
+
+ This changes how dictation starts, not how text is typed. If text
+ does not appear, see{" "}
+
+ Wayland text injection
+
+ .
+
+
+
+ );
+}
diff --git a/web/src/app/hyprland-sway/page.tsx b/web/src/app/hyprland-sway/page.tsx
new file mode 100644
index 0000000..7f74a2b
--- /dev/null
+++ b/web/src/app/hyprland-sway/page.tsx
@@ -0,0 +1,513 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import type { ReactNode } from "react";
+import {
+ AlertTriangle,
+ ChevronRight,
+ Keyboard,
+ Monitor,
+ PanelTop,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { TerminalBlock } from "@/components/terminal-block";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/hyprland-sway";
+const PAGE_TITLE = "Voice Typing on Hyprland, Sway and i3 for Linux";
+const PAGE_DESCRIPTION =
+ "Set up Vocalinux voice typing on sway, i3 and Hyprland: pick wtype, ydotool, IBus or the RemoteDesktop portal, bind keys over D-Bus, and use the Dictation Pad.";
+
+const supportRows = [
+ {
+ name: "sway",
+ status: "Good support",
+ session: "Wayland",
+ notes: "Wayland native, uses wtype",
+ },
+ { name: "i3", status: "Good support", session: "X11", notes: "Uses xdotool" },
+ {
+ name: "Hyprland",
+ status: "Experimental",
+ session: "Wayland",
+ notes: "May need wtype configuration",
+ },
+];
+
+const backendRows = [
+ {
+ backend: "IBus (ibus)",
+ where:
+ "Desktops that bridge IBus; on sway and Hyprland only with ibus-wayland running",
+ needs:
+ "IBus. On sway and Hyprland, Vocalinux skips IBus unless the ibus-wayland bridge is running",
+ },
+ {
+ backend: "RemoteDesktop portal (portal)",
+ where: "Wayland desktops whose portal implements RemoteDesktop",
+ needs:
+ "org.freedesktop.portal.RemoteDesktop. GNOME and KDE Plasma have it; most wlroots compositors do not",
+ },
+ {
+ backend: "ydotool (ydotool)",
+ where: "Any Wayland session",
+ needs: "A usable /dev/uinput and a working ydotoold setup",
+ },
+ {
+ backend: "wtype (wtype)",
+ where: "sway, Hyprland and other virtual-keyboard compositors",
+ needs: "The wtype package",
+ },
+ {
+ backend: "xdotool (xdotool)",
+ where: "i3 and other X11 sessions; XWayland windows only on Wayland",
+ needs: "The xdotool package",
+ },
+];
+
+const steps = [
+ {
+ name: "Install Vocalinux and the typing tools",
+ text: "Install Vocalinux, then make sure wtype and wl-clipboard are installed on sway or Hyprland, or xdotool on i3.",
+ },
+ {
+ name: "Check that the tool can type",
+ text: 'Focus a text field and run wtype "hello" on Wayland or xdotool type "hello" on X11. If nothing appears, Vocalinux cannot type there either.',
+ },
+ {
+ name: "Choose how dictation starts",
+ text: "Either add yourself to the input group for the built-in hotkey, or turn on External Activation and bind vocalinux --toggle in your compositor config.",
+ },
+ {
+ name: "Pin a backend if autodetection guesses wrong",
+ text: "Set text_injection.backend in ~/.config/vocalinux/config.json, for example to wtype, then restart Vocalinux.",
+ },
+];
+
+const faqs = [
+ {
+ question:
+ "Why does dictation work in my browser but not in native Wayland apps?",
+ answer:
+ "That is the classic IBus symptom: the compositor does not relay IBus commits to native Wayland clients, so IBus reports success while nothing appears. Pin the wtype backend and restart Vocalinux.",
+ },
+ {
+ question: "Does the RemoteDesktop portal work on sway or Hyprland?",
+ answer:
+ "Only if your portal backend implements org.freedesktop.portal.RemoteDesktop. GNOME and KDE Plasma do and most wlroots compositors do not. When it is missing, Vocalinux moves on to ydotool and then wtype.",
+ },
+ {
+ question: "Can I use push-to-talk on a tiling compositor?",
+ answer:
+ "Yes, with the built-in listener, which on Wayland needs your user in the input group. A compositor keybind over D-Bus can only toggle dictation.",
+ },
+ {
+ question: "Is Hyprland supported?",
+ answer:
+ "Hyprland is marked Experimental in the compatibility table and may need wtype configuration. sway and i3 are marked as good support.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: `${PAGE_TITLE} | Vocalinux`,
+ description: PAGE_DESCRIPTION,
+ path: PAGE_PATH,
+ keywords: [
+ "hyprland voice typing",
+ "sway speech to text",
+ "i3 voice dictation",
+ "wtype dictation",
+ "wayland tiling compositor dictation",
+ "hyprland dictation keybind",
+ "ydotool voice typing",
+ ],
+});
+
+function InlineCode({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+function ConfigBlock({ label, children }: { label: string; children: string }) {
+ return (
+
+ {label}
+
+ Tiling setups are hand-built, so Vocalinux cannot assume the pieces a
+ full desktop provides. This guide covers which text injection backend
+ applies on each compositor, how to bind dictation to a key in your
+ config, and the fallback that works when typing into other windows
+ does not.
+
+
+
+
+
+
+ Support status
+
+
+
+
+
+
+ Compositor / WM
+
+
+ Status
+
+
+ Session
+
+
+ Notes
+
+
+
+
+ {supportRows.map((row) => (
+
+
+ {row.name}
+
+
{row.status}
+
+ {row.session}
+
+
{row.notes}
+
+ ))}
+
+
+
+
+ Status comes from the project's desktop compatibility table. For
+ GNOME, KDE and the general Wayland picture, see{" "}
+
+ voice dictation on Wayland
+
+ .
+
+
+
+
+
+ Text injection options
+
+
+ Vocalinux picks a backend automatically. On Wayland, when IBus is not
+ selected, it tries the RemoteDesktop portal first, then ydotool, then
+ wtype, and finally xdotool for XWayland windows. sway and Hyprland are
+ on its list of compositors that do not bridge IBus to native Wayland
+ apps, so IBus is skipped there unless the{" "}
+ ibus-wayland helper (IBus 1.5.32 and newer)
+ is running.
+
+
+
+
+
+
+ Backend (config value)
+
+
+ Where it applies
+
+
+ Needs
+
+
+
+
+ {backendRows.map((row) => (
+
+
+ {row.backend}
+
+
+ {row.where}
+
+
{row.needs}
+
+ ))}
+
+
+
+
Pin a backend
+
+ Edit ~/.config/vocalinux/config.json and
+ restart Vocalinux. An explicit non-IBus value skips IBus selection. If
+ the pinned tool is missing, Vocalinux warns and falls back to its
+ normal order.
+
+ To try a backend for one run without saving it:
+
+
+
Terminals
+
+ When text goes in through the clipboard, Vocalinux sends Ctrl+V in
+ normal fields and Ctrl+Shift+V in terminal windows. Terminal detection
+ works on Hyprland, sway and niri.
+
+
+
+
+
+ Setup steps
+
+
+ {steps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
+
+
+
+
+ Keybinds via D-Bus
+
+
+ On Wayland the built-in hotkey reads{" "}
+ /dev/input, so your user must be in the{" "}
+ input group (the source installer does this;
+ .deb and .rpm packages cannot). The alternative, added in v0.18.0, is
+ to turn on Settings → Shortcuts → External Activation (Desktop
+ Shortcut) and let the compositor own the key. These binds toggle
+ dictation; they cannot do push-to-talk.
+
+ Or keep the built-in listener and join the group, then log out and
+ back in:
+
+
+
+ The full D-Bus interface and Flatpak command are in{" "}
+
+ global shortcuts
+
+ . Minimal window-manager sessions may also need their own autostart
+ line; see{" "}
+
+ autostart
+
+ .
+
+
+
+
+
+
+ The Dictation Pad fallback
+
+
+ If no backend types reliably on your setup, send dictation to the
+ Dictation Pad instead (added in v0.18.0). It is an in-app window that
+ receives the text, and you copy it out by hand, so no injection path
+ is involved. Turn on Settings → Dictation → Dictate into Dictation
+ Pad, then open it with Open Dictation Pad in the same group or
+ Dictation Pad in the tray menu. Later releases fixed the pad ghosting
+ away when idle under a Wayland compositor (v0.18.1) and stopped it
+ travelling between workspaces with Settings (v0.18.5). Details on the{" "}
+
+ Dictation Pad page
+
+ .
+
+
+
+
+
+
+ Troubleshooting
+
+
Nothing is typed anywhere
+
+ Install the Wayland tools and test them by hand, then run Vocalinux
+ with debug logging and check which backend it reports. The startup log
+ records a pin request; a live test in the target app is the real
+ confirmation.
+
+
+
+
+
+
+
+
+ ydotool is chosen but does nothing
+
+
+ ydotool needs /dev/uinput and a running
+ ydotoold. Fix that setup or pin wtype.
+
+
The hotkey never fires
+
+ Confirm you are in the input group in the
+ current session, or switch to External Activation with a compositor
+ bind. On i3 (X11) the built-in X11 listener handles the hotkey without
+ that group.
+
+ Install Vocalinux, check that wtype or xdotool can type, and bind
+ dictation to a key in your compositor config.
+
+
+
+ Install Vocalinux
+
+
+
+ Wayland guide
+
+
+
+
+ );
+}
diff --git a/web/src/app/install/[distro]/page.tsx b/web/src/app/install/[distro]/page.tsx
index 5fde2b5..f2648c7 100644
--- a/web/src/app/install/[distro]/page.tsx
+++ b/web/src/app/install/[distro]/page.tsx
@@ -5,7 +5,11 @@ import {
CheckCircle2,
ChevronRight,
Cpu,
+ Download,
Laptop,
+ Monitor,
+ RefreshCw,
+ Server,
Shield,
Sparkles,
Terminal,
@@ -13,81 +17,149 @@ import {
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
-type DistroSlug = "ubuntu" | "fedora" | "arch";
+type DistroSlug =
+ | "ubuntu"
+ | "debian"
+ | "fedora"
+ | "arch"
+ | "opensuse"
+ | "linux-mint"
+ | "pop-os";
type DistroParams = { distro: string } | Promise<{ distro: string }>;
+interface InstallOption {
+ label: string;
+ note: string;
+ command: string;
+}
+
interface DistroConfig {
name: string;
title: string;
description: string;
intro: string;
prerequisites: string[];
- testedOn: string[];
+ supportedOn: string[];
installCommand: string;
+ otherInstallOptions: InstallOption[];
setupNotes: string[];
postInstallChecks: string[];
}
+const INSTALL_SCRIPT_COMMAND = `curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh
+bash /tmp/vl.sh`;
+
+const DEB_OPTION: InstallOption = {
+ label: "Native .deb package",
+ note: "Download the .deb for your CPU from GitHub Releases (arm64 files are attached too). It is a thin package: Python and GTK come from the distro, whisper.cpp runs on the CPU, and there is no auto-update. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
+ command: "sudo apt install ./vocalinux_*_amd64.deb",
+};
+
+const RPM_OPTION: InstallOption = {
+ label: "Native .rpm package",
+ note: "Download the .rpm for your CPU from GitHub Releases (aarch64 files are attached too). It is a thin package: Python and GTK come from Fedora, whisper.cpp runs on the CPU, and there is no auto-update or COPR yet. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
+ command: "sudo dnf install ./vocalinux-*.x86_64.rpm",
+};
+
+const FLATPAK_OPTION: InstallOption = {
+ label: "Flatpak from the VocaHQ remote",
+ note: "Not on Flathub. The self-hosted vocahq remote updates with flatpak update and ships whisper.cpp only.",
+ command:
+ "flatpak install https://vocahq.github.io/vocalinux-flatpak/com.vocalinux.Vocalinux.flatpakref",
+};
+
+const COMMON_CHECKS = [
+ "Run vocalinux --version to confirm the installed release.",
+ "Launch vocalinux and confirm the tray icon appears. On GNOME, the tray needs the AppIndicator extension.",
+ "Hold Right Alt (Option) to dictate, or switch to toggle mode in Settings → Shortcuts, and confirm text lands in a terminal and a browser.",
+ "If anything misbehaves, run vocalinux --debug and check the troubleshooting guide.",
+];
+
const distroConfig: Record = {
ubuntu: {
name: "Ubuntu",
title: "Install Voice Dictation on Ubuntu",
description:
- "Step-by-step Ubuntu voice dictation setup for offline speech-to-text with whisper.cpp and VOSK. Works on Ubuntu 24.04+, GNOME, KDE, X11, and Wayland.",
+ "Step-by-step Ubuntu voice dictation setup for offline speech-to-text with whisper.cpp. Covers Ubuntu 24.04+, the install script, the .deb, X11 and Wayland.",
intro:
- "This Ubuntu guide is tuned for practical desktop use: terminals, browsers, IDEs, office suites, and chat apps. Local engines keep audio on your machine. Remote API is optional.",
+ "This Ubuntu guide is tuned for practical desktop use: terminals, browsers, IDEs, office suites, and chat apps. Local engines keep audio on your machine after the one-time model download. Remote API is optional.",
prerequisites: [
- "Ubuntu 24.04+ (or Ubuntu-based distro)",
- "Python 3.11+",
+ "Ubuntu 24.04 or newer (Ubuntu 22.04 ships Python 3.10, below the 3.11 floor)",
+ "Python 3.11+ with the distro's python3-gi (the installer handles this)",
"Working microphone",
"curl installed (sudo apt update && sudo apt install -y curl)",
],
- testedOn: [
- "Ubuntu 24.04 LTS (GNOME, Wayland)",
- "Ubuntu 24.04 LTS (GNOME, X11)",
- "Kubuntu and Linux Mint derivatives",
+ supportedOn: [
+ "Ubuntu 24.04 LTS and newer: full support",
+ "Ubuntu-based derivatives on a 24.04 base, such as Linux Mint 22, Pop!_OS 24.04, elementary OS 8 and Zorin OS on 24.04",
],
- installCommand:
- "curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --interactive",
+ installCommand: INSTALL_SCRIPT_COMMAND,
+ otherInstallOptions: [DEB_OPTION, FLATPAK_OPTION],
setupNotes: [
- "Choose whisper.cpp for the fastest setup and cross-vendor GPU support.",
- "On lower-RAM systems, pick VOSK for lightweight realtime dictation.",
- "If your desktop uses Wayland, keep keyboard injection defaults unless you know your compositor specifics.",
+ "Keep the default whisper.cpp engine: it installs in about 1-2 minutes and uses a Vulkan GPU when one is available.",
+ "On lower-RAM systems, VOSK is the lightweight option (--engine=vosk with the install script).",
+ "On KDE Plasma Wayland (Kubuntu), set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
+ "The install script adds you to the input group for Wayland hotkeys; log out and back in afterwards.",
],
postInstallChecks: [
- "Run vocalinux and confirm the tray indicator appears.",
- "Hold Right Alt (Option) to dictate, or switch to toggle mode in Settings and verify dictation starts.",
- "Set language and model size in the settings panel for your workload.",
+ ...COMMON_CHECKS,
+ "Set language and model size in Settings → Speech Model for your workload.",
+ ],
+ },
+ debian: {
+ name: "Debian",
+ title: "Install Voice Dictation on Debian",
+ description:
+ "Install Vocalinux offline voice dictation on Debian 12, 13 and Testing with the install script or the native .deb. Covers AppIndicator, ydotool and Wayland.",
+ intro:
+ "Debian 12 and newer are a primary target for Vocalinux. The install script detects Debian, installs the system packages it needs, and sets up whisper.cpp with a checksum-verified tiny model.",
+ prerequisites: [
+ "Debian 12 or newer, including Testing (Debian 11 and earlier are below the Python 3.11 floor)",
+ "Python 3.11+ with the distro's python3-gi (the installer handles this)",
+ "Working microphone",
+ "curl installed (sudo apt update && sudo apt install -y curl)",
+ ],
+ supportedOn: [
+ "Debian 12 and newer: full support",
+ "Debian Testing: full support",
+ ],
+ installCommand: INSTALL_SCRIPT_COMMAND,
+ otherInstallOptions: [DEB_OPTION, FLATPAK_OPTION],
+ setupNotes: [
+ "Debian uses the Ayatana AppIndicator for the tray (gir1.2-ayatanaappindicator3-0.1); the installer picks it.",
+ "Clean Debian installs lack some build tools for compiling pywhispercpp (libssl-dev, autoconf, automake, libtool, patchelf). The installer adds them; add them yourself before a manual rebuild.",
+ "ydotool is not in Debian's standard repositories. Vocalinux falls back to IBus or wtype for most Wayland compositors.",
+ "On KDE Plasma Wayland, set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
+ ],
+ postInstallChecks: [
+ ...COMMON_CHECKS,
+ "On GNOME, install gnome-shell-extension-appindicator if the tray icon never appears.",
],
},
fedora: {
name: "Fedora",
title: "Install Voice Dictation on Fedora",
description:
- "Fedora speech-to-text installation guide for Vocalinux. Set up offline Linux voice typing on Fedora Workstation with Wayland or X11.",
+ "Fedora speech-to-text installation guide for Vocalinux. Set up offline Linux voice typing on Fedora 42+ with the install script or .rpm, on Wayland or X11.",
intro:
- "Fedora is an excellent match for Vocalinux thanks to current Python stacks and up-to-date system libraries. This guide focuses on reliable setup and minimal friction.",
+ "Fedora 42 and newer ship the Python and GTK stack Vocalinux needs, and Fedora 42 is the release in the project's CI matrix. This guide focuses on reliable setup and minimal friction.",
prerequisites: [
- "Fedora Workstation 39+ (or Fedora-based distro)",
- "Python 3.11+",
+ "Fedora 42 or newer (Workstation or a spin)",
+ "Python 3.11+ with python3-gobject (the installer handles this)",
"Working microphone",
"curl installed (sudo dnf install -y curl)",
],
- testedOn: [
- "Fedora Workstation 41 (Wayland)",
- "Fedora Workstation 40 (X11 fallback)",
- "Fedora KDE spin",
- ],
- installCommand:
- "curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --interactive",
+ supportedOn: ["Fedora 42 and newer: good support (uses lib64 paths)"],
+ installCommand: INSTALL_SCRIPT_COMMAND,
+ otherInstallOptions: [RPM_OPTION, FLATPAK_OPTION],
setupNotes: [
"If prompted for optional dependencies, accept defaults unless your desktop setup requires custom input tooling.",
- "Keep the default whisper.cpp engine for best speed-to-accuracy tradeoff.",
- "Use the smallest model first, then scale up after confirming stable dictation latency.",
+ "Keep the default whisper.cpp engine for the best speed-to-accuracy tradeoff.",
+ "Use the smallest model first, then scale up once dictation feels stable on your hardware.",
+ "On the KDE spin under Wayland, set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
],
postInstallChecks: [
- "Run vocalinux from terminal and test transcription in GNOME Text Editor or VS Code.",
- "Verify toggle mode and push-to-talk behavior with your preferred keyboard layout.",
+ ...COMMON_CHECKS,
"Confirm startup behavior and language settings persist after reboot.",
],
},
@@ -95,33 +167,135 @@ const distroConfig: Record = {
name: "Arch Linux",
title: "Install Voice Dictation on Arch Linux",
description:
- "Arch Linux voice dictation guide for offline speech recognition. Install Vocalinux on Arch, Manjaro, and EndeavourOS with distro-aware setup notes.",
+ "Arch Linux voice dictation guide for offline speech recognition. Install Vocalinux from the AUR or the install script on Arch, Manjaro and EndeavourOS.",
intro:
- "Arch users usually care about control and performance. This guide keeps setup lean while preserving compatibility across rolling-release desktop environments.",
+ "Arch users can install Vocalinux from the AUR or with the install script. Both keep whisper.cpp as the default engine and work across rolling-release desktops.",
prerequisites: [
- "Arch Linux / Manjaro / EndeavourOS",
- "Python 3.11+",
+ "Arch Linux, Manjaro or EndeavourOS",
+ "Python 3.11+ with python-gobject",
"Working microphone",
- "curl installed (sudo pacman -Sy --needed curl)",
+ "curl installed (sudo pacman -Sy --needed curl), or an AUR helper such as yay",
],
- testedOn: [
- "Arch Linux (GNOME, Wayland)",
- "Arch Linux (KDE Plasma)",
- "Manjaro (X11 and Wayland sessions)",
+ supportedOn: [
+ "Arch Linux (rolling): good support",
+ "Manjaro (rolling): good support, inherits Arch compatibility",
+ "EndeavourOS (rolling): good support, inherits Arch compatibility",
+ ],
+ installCommand: INSTALL_SCRIPT_COMMAND,
+ otherInstallOptions: [
+ {
+ label: "AUR packages",
+ note: "vocalinux builds the canonical release sdist against Arch dependencies. vocalinux-bin installs the prebuilt release AppImage. vocalinux-git is a community package that tracks main. The three conflict, so install only one.",
+ command: `yay -S vocalinux # canonical release sdist, built against Arch dependencies
+yay -S vocalinux-git # community package; tracks main`,
+ },
],
- installCommand:
- "curl -fsSL https://raw.githubusercontent.com/VocaHQ/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --interactive",
setupNotes: [
- "Prefer whisper.cpp for lower dependency footprint and faster cold start.",
+ "Prefer whisper.cpp: it avoids the full PyTorch stack that OpenAI Whisper pulls in.",
"On custom compositor setups, verify text injection behavior in both terminal and browser contexts.",
"If you tweak keybinds heavily, set a non-conflicting activation shortcut in Vocalinux settings.",
+ "AUR packages update with your AUR helper; the in-app update checker only notifies.",
],
postInstallChecks: [
- "Run vocalinux and test dictation in multiple apps (terminal, browser, editor).",
- "Confirm your chosen speech engine model balances latency and accuracy.",
+ ...COMMON_CHECKS,
"Back up ~/.config/vocalinux/config.json after tuning preferences.",
],
},
+ opensuse: {
+ name: "openSUSE",
+ title: "Install Voice Dictation on openSUSE",
+ description:
+ "Install Vocalinux offline voice dictation on openSUSE Tumbleweed with the install script. Covers versioned Python packages, Vulkan builds, X11 and Wayland.",
+ intro:
+ "On openSUSE Tumbleweed the install script uses zypper, resolves the versioned Python packages for your snapshot, and sets up whisper.cpp with a checksum-verified tiny model.",
+ prerequisites: [
+ "openSUSE Tumbleweed",
+ "Python 3.11+ with the matching pythonXY-gobject package (the installer handles this)",
+ "Working microphone",
+ "curl installed (sudo zypper install curl)",
+ ],
+ supportedOn: [
+ "openSUSE Tumbleweed: good support",
+ "openSUSE Leap is not listed in the compatibility matrix",
+ ],
+ installCommand: INSTALL_SCRIPT_COMMAND,
+ otherInstallOptions: [
+ FLATPAK_OPTION,
+ {
+ label: "AppImage",
+ note: "Download the x86_64 or aarch64 AppImage from GitHub Releases. It starts on Tumbleweed and uses Vulkan through the host driver, but still needs host text-injection tools (xdotool on X11; wtype, ydotool or clipboard tools on Wayland).",
+ command: `chmod +x Vocalinux-*-x86_64.AppImage # or aarch64
+./Vocalinux-*-x86_64.AppImage`,
+ },
+ ],
+ setupNotes: [
+ "Tumbleweed uses versioned Python package names such as python313-devel. The -devel suffix means development headers, not beta packages.",
+ "For a manual install, if ${PYVER}-virtualenv is unavailable on your snapshot, try ${PYVER}-venv.",
+ "For whisper.cpp Vulkan GPU builds, install vulkan-tools, vulkan-devel and shaderc (sudo zypper install -y vulkan-tools vulkan-devel shaderc).",
+ "On KDE Plasma Wayland, set System Settings → Keyboard → Virtual Keyboard to IBus Wayland.",
+ ],
+ postInstallChecks: [
+ ...COMMON_CHECKS,
+ "Run vulkaninfo --summary if you expect GPU acceleration.",
+ ],
+ },
+ "linux-mint": {
+ name: "Linux Mint",
+ title: "Install Voice Dictation on Linux Mint",
+ description:
+ "Install Vocalinux offline voice dictation on Linux Mint 22 with the install script or .deb. Covers Cinnamon, the Ubuntu 24.04 base and why Mint 21 is too old.",
+ intro:
+ "Linux Mint is judged by its Ubuntu base, not its own version number. Mint 22 is built on Ubuntu 24.04, so the install script treats it as Ubuntu and installs everything Vocalinux needs.",
+ prerequisites: [
+ "Linux Mint 22 or newer, on an Ubuntu 24.04+ base (Mint 21 is on Ubuntu 22.04, below the floor)",
+ "Python 3.11+ with the distro's python3-gi (the installer handles this)",
+ "Working microphone",
+ "curl installed (sudo apt update && sudo apt install -y curl)",
+ ],
+ supportedOn: [
+ "Linux Mint on Ubuntu 24.04+ (Mint 22): good support",
+ "Cinnamon, MATE and Xfce desktops: full support",
+ ],
+ installCommand: INSTALL_SCRIPT_COMMAND,
+ otherInstallOptions: [
+ {
+ ...DEB_OPTION,
+ note: `The .deb targets Debian 12+ and Ubuntu 24.04+, the base Mint 22 is built on. ${DEB_OPTION.note}`,
+ },
+ FLATPAK_OPTION,
+ ],
+ setupNotes: [
+ "Keep the default whisper.cpp engine unless you have a specific reason to switch.",
+ "Cinnamon is fully supported and works with AppIndicator for the tray icon.",
+ "Text is typed with xdotool on X11 sessions; on Wayland, Vocalinux picks from IBus, the RemoteDesktop portal, wtype or ydotool.",
+ "The install script adds you to the input group for Wayland hotkeys; log out and back in afterwards.",
+ ],
+ postInstallChecks: [...COMMON_CHECKS],
+ },
+ "pop-os": {
+ name: "Pop!_OS",
+ title: "Install Voice Dictation on Pop!_OS",
+ description:
+ "Install Vocalinux offline voice dictation on Pop!_OS 24.04 with the install script or .deb. Covers the Ubuntu base, Wayland hotkeys and fallback options.",
+ intro:
+ "Pop!_OS uses Ubuntu's own version numbering, and 24.04 and newer meet the Python 3.11 floor. The install script detects Pop!_OS as part of the Ubuntu family.",
+ prerequisites: [
+ "Pop!_OS 24.04 or newer",
+ "Python 3.11+ with the distro's python3-gi (the installer handles this)",
+ "Working microphone",
+ "curl installed (sudo apt update && sudo apt install -y curl)",
+ ],
+ supportedOn: ["Pop!_OS 24.04 and newer: good support"],
+ installCommand: INSTALL_SCRIPT_COMMAND,
+ otherInstallOptions: [DEB_OPTION, FLATPAK_OPTION],
+ setupNotes: [
+ "For a manual install, use libgirepository-2.0-dev if libgirepository1.0-dev is not available.",
+ "The install script adds you to the input group for Wayland hotkeys; log out and back in afterwards.",
+ "If the hotkey or typing does not work in your session, enable Settings → Shortcuts → External Activation (Desktop Shortcut) and bind a desktop shortcut to vocalinux --toggle (toggle only, no push-to-talk), or dictate into the Dictation Pad and copy the text out.",
+ "whisper.cpp uses a Vulkan GPU when available; check with vulkaninfo --summary.",
+ ],
+ postInstallChecks: [...COMMON_CHECKS],
+ },
};
const distroKeywords: Record = {
@@ -131,6 +305,12 @@ const distroKeywords: Record = {
"ubuntu voice typing",
"offline dictation ubuntu",
],
+ debian: [
+ "debian voice dictation",
+ "speech to text debian",
+ "debian voice typing",
+ "offline dictation debian",
+ ],
fedora: [
"fedora voice dictation",
"speech to text fedora",
@@ -141,7 +321,25 @@ const distroKeywords: Record = {
"arch linux voice dictation",
"speech to text arch linux",
"manjaro dictation",
- "offline dictation arch",
+ "vocalinux aur",
+ ],
+ opensuse: [
+ "opensuse voice dictation",
+ "speech to text opensuse tumbleweed",
+ "opensuse voice typing",
+ "offline dictation opensuse",
+ ],
+ "linux-mint": [
+ "linux mint voice dictation",
+ "speech to text linux mint",
+ "linux mint voice typing",
+ "cinnamon dictation",
+ ],
+ "pop-os": [
+ "pop os voice dictation",
+ "speech to text pop!_os",
+ "pop os voice typing",
+ "offline dictation pop os",
],
};
@@ -149,35 +347,65 @@ const isDistroSlug = (value: string): value is DistroSlug => {
return value in distroConfig;
};
-const resolveParams = async (params: DistroParams): Promise<{ distro: string }> => {
+const resolveParams = async (
+ params: DistroParams,
+): Promise<{ distro: string }> => {
return Promise.resolve(params);
};
const getDistroStyles = (distro: DistroSlug) => {
- if (distro === "ubuntu") {
- return {
- icon: Laptop,
- iconClass: "text-primary",
- chipClass: "border-border bg-muted text-muted-foreground",
- panelClass: "bg-primary/10",
- };
+ switch (distro) {
+ case "ubuntu":
+ return {
+ icon: Laptop,
+ iconClass: "text-primary",
+ chipClass: "border-border bg-muted text-muted-foreground",
+ panelClass: "bg-primary/10",
+ };
+ case "debian":
+ return {
+ icon: Server,
+ iconClass: "text-primary",
+ chipClass: "border-border bg-muted text-muted-foreground",
+ panelClass: "bg-primary/10",
+ };
+ case "fedora":
+ return {
+ icon: Sparkles,
+ iconClass: "text-primary",
+ chipClass: "border-border bg-primary/10 text-primary",
+ panelClass: "bg-primary/10",
+ };
+ case "opensuse":
+ return {
+ icon: RefreshCw,
+ iconClass: "text-primary",
+ chipClass: "border-border bg-primary/10 text-primary",
+ panelClass: "bg-primary/10",
+ };
+ case "linux-mint":
+ return {
+ icon: Monitor,
+ iconClass: "text-primary",
+ chipClass: "border-border bg-muted text-muted-foreground",
+ panelClass: "bg-primary/10",
+ };
+ case "pop-os":
+ return {
+ icon: Laptop,
+ iconClass: "text-primary",
+ chipClass: "border-border bg-primary/10 text-primary",
+ panelClass: "bg-primary/10",
+ };
+ case "arch":
+ default:
+ return {
+ icon: Cpu,
+ iconClass: "text-primary",
+ chipClass: "border-border bg-primary/10 text-muted-foreground",
+ panelClass: "bg-primary/10",
+ };
}
-
- if (distro === "fedora") {
- return {
- icon: Sparkles,
- iconClass: "text-primary",
- chipClass: "border-border bg-primary/10 text-primary",
- panelClass: "bg-primary/10",
- };
- }
-
- return {
- icon: Cpu,
- iconClass: "text-primary",
- chipClass: "border-border bg-primary/10 text-muted-foreground",
- panelClass: "bg-primary/10",
- };
};
export function generateStaticParams() {
@@ -195,7 +423,7 @@ export async function generateMetadata({
return buildPageMetadata({
title: "Linux Voice Dictation Install Guide",
description:
- "Install Vocalinux on Linux with distro-specific setup instructions for Ubuntu, Fedora, and Arch.",
+ "Install Vocalinux on Linux with distro-specific setup instructions for Ubuntu, Debian, Fedora, Arch, openSUSE, Linux Mint, and Pop!_OS.",
path: "/install",
});
}
@@ -277,18 +505,6 @@ export default async function DistroInstallPage({
dangerouslySetInnerHTML={{ __html: JSON.stringify(howToJsonLd) }}
/>
-
-
-
{config.title}
-
{config.intro}
+
+ {config.title}
+
+
+ {config.intro}
+
@@ -333,20 +553,69 @@ export default async function DistroInstallPage({
Need help selecting an engine? Read the
-
- whisper.cpp vs Whisper vs VOSK comparison
+
+ engine comparison
+
+ . Stuck? See
+
+ troubleshooting
.
diff --git a/web/src/app/install/nixos/page.tsx b/web/src/app/install/nixos/page.tsx
new file mode 100644
index 0000000..493e9cf
--- /dev/null
+++ b/web/src/app/install/nixos/page.tsx
@@ -0,0 +1,418 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import {
+ AlertTriangle,
+ Braces,
+ CheckCircle2,
+ ChevronRight,
+ CircleHelp,
+ Settings2,
+ Terminal,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/install/nixos";
+const PAGE_TITLE = "Vocalinux on NixOS: Nix Flake and NixOS Module | Vocalinux";
+const PAGE_DESCRIPTION =
+ "Install Vocalinux on NixOS or any Nix system with the official flake: nix run, nix profile, the NixOS and Home Manager modules, Wayland groups and scope.";
+
+const TRY_COMMANDS = `nix run github:VocaHQ/vocalinux # try it
+nix profile install github:VocaHQ/vocalinux`;
+
+const NIXOS_SNIPPET = `{
+ inputs.vocalinux.url = "github:VocaHQ/vocalinux";
+
+ outputs = { nixpkgs, vocalinux, ... }: {
+ nixosConfigurations.host = nixpkgs.lib.nixosSystem {
+ modules = [
+ vocalinux.nixosModules.vocalinux
+ {
+ programs.vocalinux.enable = true;
+ users.users.you.extraGroups = [ "input" "uinput" ];
+ }
+ ];
+ };
+ };
+}`;
+
+const HOME_MANAGER_SNIPPET = `{
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+ inputs.home-manager.url = "github:nix-community/home-manager";
+ inputs.home-manager.inputs.nixpkgs.follows = "nixpkgs";
+ inputs.vocalinux.url = "github:VocaHQ/vocalinux";
+
+ outputs = { nixpkgs, home-manager, vocalinux, ... }: {
+ homeConfigurations.you = home-manager.lib.homeManagerConfiguration {
+ pkgs = nixpkgs.legacyPackages.x86_64-linux;
+ modules = [
+ vocalinux.homeManagerModules.vocalinux
+ { programs.vocalinux.enable = true; }
+ ];
+ };
+ };
+}`;
+
+const flakeOutputs = [
+ {
+ output: "packages..vocalinux (also default)",
+ purpose: "The Vocalinux package for x86_64-linux and aarch64-linux",
+ },
+ {
+ output: "apps..vocalinux (also default)",
+ purpose: "What nix run github:VocaHQ/vocalinux starts",
+ },
+ {
+ output: "overlays.default",
+ purpose: "Adds pkgs.vocalinux to your own nixpkgs",
+ },
+ {
+ output: "nixosModules.vocalinux (also default)",
+ purpose: "programs.vocalinux.enable and programs.vocalinux.package",
+ },
+ {
+ output: "homeManagerModules.vocalinux (also default)",
+ purpose: "The same programs.vocalinux options, per user",
+ },
+];
+
+const moduleSteps = [
+ {
+ name: "Add the flake input",
+ text: 'Add inputs.vocalinux.url = "github:VocaHQ/vocalinux" to your system flake.',
+ },
+ {
+ name: "Import the module and enable it",
+ text: "Add vocalinux.nixosModules.vocalinux to your modules and set programs.vocalinux.enable = true. The module installs the package and enables hardware.uinput.",
+ },
+ {
+ name: "Add each dictating user to input and uinput",
+ text: 'Set users.users..extraGroups = [ "input" "uinput" ] for Wayland shortcuts and ydotool typing. X11 sessions can skip this.',
+ },
+ {
+ name: "Rebuild and log in again",
+ text: "Rebuild your system as usual, then log out and back in so the new group membership applies.",
+ },
+];
+
+const faqs = [
+ {
+ question: "Is NixOS supported?",
+ answer:
+ "Yes, through the Nix flake added in v0.18.2. The standard install.sh script does not work on NixOS because of the /nix/store layout, which is why the distro compatibility doc still lists NixOS as unsupported for that installer. Use the flake instead.",
+ },
+ {
+ question: "Does the Nix package use my GPU?",
+ answer:
+ "The bundled pywhispercpp is built with the Vulkan backend, so whisper.cpp can use an AMD, Intel or NVIDIA GPU through Vulkan. It is also built with GGML_NATIVE=OFF, a portable baseline rather than -march=native.",
+ },
+ {
+ question:
+ "Can I use Parakeet, Faster Whisper, VOSK or OpenAI Whisper from the flake?",
+ answer:
+ "No. The flake packages whisper.cpp plus the Silero VAD extra. The other engines are not packaged because of the PyTorch footprint or because they are not in nixpkgs.",
+ },
+ {
+ question: "Why do I need the input group?",
+ answer:
+ "On Wayland the built-in push-to-talk hotkey reads keyboard devices under /dev/input. Membership in input grants read access to every keyboard and pointer device, so the module leaves that choice to each user. The alternative is a compositor shortcut bound to the D-Bus Toggle command, which only toggles.",
+ },
+ {
+ question: "How do I update?",
+ answer:
+ "Update the vocalinux input in your flake lock and rebuild, or upgrade your Nix profile. The in-app update checker only tells you a release exists; it never installs anything.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: PAGE_TITLE,
+ description: PAGE_DESCRIPTION,
+ path: PAGE_PATH,
+ keywords: [
+ "nixos voice dictation",
+ "vocalinux nix flake",
+ "nixos speech to text",
+ "nixos module vocalinux",
+ "home manager vocalinux",
+ "nix whisper.cpp dictation",
+ ],
+});
+
+function Commands({ children }: { children: string }) {
+ return (
+
+ The Vocalinux repository is a Nix flake. It builds the app against
+ nixpkgs, ships a NixOS module and a Home Manager module, and wires
+ every text-injection and clipboard tool onto the wrapped
+ binary’s PATH. Use it on NixOS, or on any Linux with Nix and
+ flakes enabled.
+
+
+
+
+
+
+ Try it or install into a profile
+
+
+ No configuration needed. nix run starts Vocalinux once;{" "}
+ nix profile install keeps it in your user profile:
+
+ {TRY_COMMANDS}
+
+ On a non-NixOS host, pair nix profile install with your
+ distro’s udev rule and group membership for{" "}
+ /dev/uinput and /dev/input if you use
+ Wayland.
+
+
+
+
+
+
+ Enable the NixOS module
+
+
+ {moduleSteps.map((step, index) => (
+
+
+ {index + 1}. {step.name}
+
+
{step.text}
+
+ ))}
+
+
+ The documented flake, with host and you{" "}
+ replaced by your hostname and user:
+
+ {NIXOS_SNIPPET}
+
+ hardware.uinput.enable, which the module sets, loads the
+ uinput kernel module, creates the uinput group and
+ installs the matching udev rule. ydotool and the evdev backend’s
+ keyboard clone both open /dev/uinput. Group membership
+ stays per user because input grants read access to every
+ keyboard and pointer device.
+
+
+
Home Manager
+
+ homeManagerModules.vocalinux takes the same{" "}
+ programs.vocalinux options and installs into your home
+ profile. Device permissions are a system concern, so on NixOS keep the
+ NixOS module (or the group lines) on the system side.
+
+ {HOME_MANAGER_SNIPPET}
+
+
+
+
+
+ What the flake provides
+
+
+
+
+
+
Flake output
+
Use
+
+
+
+ {flakeOutputs.map((row) => (
+
+
{row.output}
+
{row.purpose}
+
+ ))}
+
+
+
+
Included in the package
+
+ {[
+ "whisper.cpp (the default engine), with pywhispercpp built from its PyPI sdist with the Vulkan backend",
+ "Silero neural VAD through onnxruntime (the [vad] extra)",
+ "GTK 3, PyGObject and the AppIndicator typelib from nixpkgs",
+ "xdotool, wtype, ydotool, wl-clipboard, xclip, xsel, xprop, setxkbmap, ibus, gdbus, notify-send, paplay, aplay and vulkaninfo on PATH",
+ "A desktop entry and the app icon",
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
Not included
+
+ The vosk, whisper, parakeet and{" "}
+ faster_whisper extras are not packaged, so{" "}
+ Parakeet and{" "}
+ Faster Whisper need the install
+ script on another distro. The flake follows{" "}
+ nixos-unstable and builds for x86_64-linux and
+ aarch64-linux.
+
+
+
+
+
+
+ Gotchas
+
+
+
+ Do not run install.sh on NixOS. It expects a
+ conventional filesystem layout; the flake is the supported path.
+
+
+ Speech models are not part of the Nix store. Vocalinux downloads one
+ only after you confirm it, and shows the size first.
+
+
+ On Wayland without input membership, the hotkey is
+ disabled. Bind a compositor shortcut through{" "}
+ D-Bus activation instead; it
+ toggles, it cannot push-to-talk.
+
+
+ On KDE Plasma Wayland, set System Settings → Keyboard → Virtual
+ Keyboard to “IBus Wayland”. More in the{" "}
+ Wayland guide.
+
+
+
+
+
+
+
+ NixOS FAQ
+
+
+ {faqs.map((faq) => (
+
+
{faq.question}
+
{faq.answer}
+
+ ))}
+
+
+
+
+
+ Other ways to install
+
+
+ Not on Nix? The install script, AUR, .deb, .rpm, AppImage and the
+ VocaHQ Flatpak remote cover most other systems.
+
+ Vocalinux can start{" "}
+
+ VocaGateway
+
+ , the self-hosted speech gateway in the Voca family, as a podman or
+ docker container from its own Settings. It is a power-user path for
+ people who want one transcription service shared by the desktop and a
+ paired phone.
+
+
+
+
+
+
+ Gateway mode is not the on-device engine
+
+
+ With a local engine, audio is transcribed on this machine by Vocalinux
+ itself. In gateway mode it is sent over HTTP to the gateway, even when
+ that gateway runs on 127.0.0.1. If you point
+ Vocalinux at a gateway on another machine, your audio leaves this
+ machine for that endpoint. Keep it on a trusted LAN, an encrypted
+ private network such as Tailscale, or HTTPS, and never expose port
+ 8765 to the public internet. Enabling the gateway does not change the
+ default engine; whisper.cpp stays on-device until you click Use this
+ Gateway.
+
+
+
+
+
+ Where your audio goes
+
+
+
+
+
+
+ Mode
+
+
+ Audio
+
+
+ Setup
+
+
+ Network
+
+
+
+
+ {comparisonRows.map((row) => (
+
+
+ {row.mode}
+
+
+ {row.audio}
+
+
+ {row.setup}
+
+
{row.network}
+
+ ))}
+
+
+
+
+
+
+
+
+ Requirements
+
+
+
+ podman (preferred) or docker, with a working Compose plugin, or{" "}
+ podman-compose /{" "}
+ docker-compose. If the engine is present
+ but Compose is missing, Settings tells you which to install.
+
+
+ A native install or the AppImage. The AppImage does not bundle a
+ container engine; it uses the host's podman or docker socket
+ when available.
+
+
+ Not the Flatpak: its sandbox cannot reach the host container socket,
+ so the controls are blocked there.
+
+
+
+
+
+
Set it up
+
+ {steps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
Status labels
+
+
+
+ {statusRows.map((row) => (
+
+
+ {row.status}
+
+
{row.meaning}
+
+ ))}
+
+
+
+
+ Pairable can appear before Ready, so a phone can pair while a model is
+ still downloading.
+
+
+
+
+
+ What Vocalinux sets up
+
+
+
+ A checkout of VocaGateway pinned to the{" "}
+ v0.1.0 tag under your XDG cache, run as the
+ Compose project vocagateway with the{" "}
+ cpu profile. It never floats on{" "}
+ latest;{" "}
+ VOCAGATEWAY_IMAGE overrides the image.
+
+
+ A private bootstrap token in{" "}
+ ~/.config/vocalinux/gateway_embed/ with
+ mode 600, plus a matching Compose .env.
+
+
+ A port published on 127.0.0.1:8765 by
+ default, reachable from this desktop only.
+
+
+ The usual Remote API settings. Use this Gateway writes the URL,
+ token, endpoint and the model name{" "}
+ whisper-1 into the same{" "}
+ remote_api_* keys described on the{" "}
+
+ Remote API page
+
+ . The token is stored in plain JSON in{" "}
+ config.json, like any Remote API key.
+
+
+
+
+
+
+
+ LAN access, stopping and gotchas
+
+
Allow LAN access for Phone
+
+ This switch publishes the gateway on{" "}
+ 0.0.0.0:8765 and advertises a LAN URL for
+ phone pairing. Open port 8765 in your firewall only on networks you
+ trust. If the switch fails, Compose keeps the previous bind and the
+ switch snaps back with an error; stop and run again, or retry.
+
+
Quit does not stop it
+
+ The container keeps running after you quit Vocalinux, including a
+ LAN-published one. Use Stop local Gateway in Settings or the tray
+ menu, which appears whenever Compose is running. Stopping keeps the
+ named data volume, so downloaded models survive. Vocalinux does not
+ start the gateway on login.
+
+
Other machines
+
+ You can also run VocaGateway on a stronger box and enter its address
+ in Remote Server by hand. Audio then crosses your network, so prefer a
+ trusted LAN, Tailscale or HTTPS. Read the{" "}
+
+ VocaGateway documentation
+
+ {" "}
+ for server-side setup.
+
+
+
+ );
+}
diff --git a/web/src/app/multilingual-dictation/page.tsx b/web/src/app/multilingual-dictation/page.tsx
new file mode 100644
index 0000000..58b9003
--- /dev/null
+++ b/web/src/app/multilingual-dictation/page.tsx
@@ -0,0 +1,495 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import {
+ CheckCircle2,
+ ChevronRight,
+ Globe,
+ Keyboard,
+ Languages,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/multilingual-dictation";
+
+// Mirrors SUPPORTED_LANGUAGES in src/vocalinux/utils/vosk_model_info.py (v0.18.5).
+const catalog: { code: string; name: string; vosk: boolean }[] = [
+ { code: "en-us", name: "English (US)", vosk: true },
+ { code: "en-in", name: "English (India)", vosk: true },
+ { code: "ar", name: "Arabic", vosk: true },
+ { code: "bn", name: "Bengali", vosk: false },
+ { code: "ca", name: "Catalan", vosk: true },
+ { code: "zh", name: "Chinese", vosk: true },
+ { code: "cs", name: "Czech", vosk: true },
+ { code: "da", name: "Danish", vosk: false },
+ { code: "nl", name: "Dutch", vosk: true },
+ { code: "fi", name: "Finnish", vosk: false },
+ { code: "fr", name: "French", vosk: true },
+ { code: "de", name: "German", vosk: true },
+ { code: "el", name: "Greek", vosk: false },
+ { code: "he", name: "Hebrew", vosk: false },
+ { code: "hi", name: "Hindi", vosk: true },
+ { code: "hu", name: "Hungarian", vosk: false },
+ { code: "id", name: "Indonesian", vosk: false },
+ { code: "it", name: "Italian", vosk: true },
+ { code: "ja", name: "Japanese", vosk: true },
+ { code: "ko", name: "Korean", vosk: true },
+ { code: "no", name: "Norwegian", vosk: false },
+ { code: "fa", name: "Persian", vosk: true },
+ { code: "pl", name: "Polish", vosk: true },
+ { code: "pt", name: "Portuguese", vosk: true },
+ { code: "ro", name: "Romanian", vosk: false },
+ { code: "ru", name: "Russian", vosk: true },
+ { code: "sl", name: "Slovenian", vosk: false },
+ { code: "es", name: "Spanish", vosk: true },
+ { code: "sv", name: "Swedish", vosk: true },
+ { code: "ta", name: "Tamil", vosk: false },
+ { code: "th", name: "Thai", vosk: false },
+ { code: "tr", name: "Turkish", vosk: true },
+ { code: "uk", name: "Ukrainian", vosk: true },
+ { code: "vi", name: "Vietnamese", vosk: true },
+];
+
+const engineRows = [
+ {
+ engine: "whisper.cpp (default)",
+ picker: "Full catalog",
+ layout: "Yes",
+ shortcuts: "Yes",
+ note: "Needs a multilingual model, not a .en one. Also offers Language Candidates.",
+ },
+ {
+ engine: "Whisper, Faster Whisper",
+ picker: "Full catalog",
+ layout: "Yes",
+ shortcuts: "Yes",
+ note: "Needs a multilingual model.",
+ },
+ {
+ engine: "Remote API",
+ picker: "Full catalog",
+ layout: "Yes",
+ shortcuts: "Yes",
+ note: "The language is sent with each request to the server you configured.",
+ },
+ {
+ engine: "VOSK",
+ picker: "Languages with an official model",
+ layout: "No",
+ shortcuts: "No",
+ note: "One model per language, so switching means a model reload.",
+ },
+ {
+ engine: "Parakeet",
+ picker: "Not used",
+ layout: "No",
+ shortcuts: "No",
+ note: "Language comes from the model: v3-european or v2-english.",
+ },
+];
+
+const howToSteps = [
+ {
+ name: "Use a multilingual model",
+ text: "In Settings → Speech Model, pick a language other than English or a multilingual model. English-only models, the ones with .en in the name, cannot switch language.",
+ },
+ {
+ name: "Add a language shortcut",
+ text: "Open Settings → Dictation, find Language Shortcuts and click Add Language Shortcut.",
+ },
+ {
+ name: "Pick the language and record a key",
+ text: "Choose the language in the new row, click Record and press a modifier plus a key such as ctrl+alt+d, or a function key such as f10.",
+ },
+ {
+ name: "Dictate with it",
+ text: "Press the language shortcut instead of your main one. It follows the shortcut mode above it, so hold it for push-to-talk or press it to toggle. Your main language comes back after that dictation.",
+ },
+];
+
+const faqs = [
+ {
+ q: "How many languages does Vocalinux support?",
+ a: "The language catalog has 33 languages plus Auto-detect. English appears twice, as English (US) and English (India), so the list has 34 entries. Slovenian was added in v0.18.4.",
+ },
+ {
+ q: "Can Vocalinux switch language in the middle of a sentence?",
+ a: "No. Whisper-family engines take one language per dictation, or detect it once per utterance on Auto-detect. For mixed-language work, use Follow keyboard layout or a language shortcut so each dictation starts in the right language.",
+ },
+ {
+ q: "Follow keyboard layout always picks the same language. Why?",
+ a: "Reading the active layout uses GNOME's input-source settings. On other desktops Vocalinux falls back to the configured primary layout, which does not change when you switch. A layout that maps to no catalog language falls back to auto-detect.",
+ },
+ {
+ q: "My language shortcut plays an error sound. What is wrong?",
+ a: "The loaded model cannot dictate in that language without a reload: an English-only Whisper model, a VOSK model for another language, or Parakeet. Vocalinux refuses rather than dictating in the wrong language. Choose a multilingual Whisper-family model.",
+ },
+ {
+ q: "Do voice commands work in other languages?",
+ a: "Punctuation and line-break phrases also match in Italian, French, German, Spanish, Portuguese, Dutch, Polish and Russian. Editing commands such as delete that and undo are English-only.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: "Multilingual Voice Dictation on Linux: 33 Languages | Vocalinux",
+ description:
+ "Dictate in more than one language on Linux: per-language shortcuts, follow your keyboard layout, 33 languages plus auto-detect, and per-engine limits.",
+ path: PAGE_PATH,
+ keywords: [
+ "multilingual dictation Linux",
+ "bilingual voice typing Linux",
+ "switch dictation language shortcut",
+ "follow keyboard layout dictation",
+ "Whisper multilingual dictation",
+ "offline speech to text languages Linux",
+ ],
+});
+
+export default function MultilingualDictationPage() {
+ const articleJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "TechArticle",
+ headline: "Multilingual Voice Dictation on Linux with Vocalinux",
+ description:
+ "How to dictate in more than one language with Vocalinux: the language catalog, per-language shortcuts, following the keyboard layout and engine caveats.",
+ dateModified: "2026-11-01",
+ author: {
+ "@type": "Person",
+ name: "Jatin K Malik",
+ url: "https://github.com/jatinkrmalik",
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "Vocalinux",
+ logo: {
+ "@type": "ImageObject",
+ url: absoluteUrl("/vocalinux.png"),
+ },
+ },
+ mainEntityOfPage: absoluteUrl(PAGE_PATH),
+ };
+
+ const howToJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "HowTo",
+ name: "Set up a per-language dictation shortcut in Vocalinux",
+ step: howToSteps.map((step, index) => ({
+ "@type": "HowToStep",
+ position: index + 1,
+ name: step.name,
+ text: step.text,
+ })),
+ };
+
+ const faqJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: faqs.map((item) => ({
+ "@type": "Question",
+ name: item.q,
+ acceptedAnswer: { "@type": "Answer", text: item.a },
+ })),
+ };
+
+ return (
+
+
+
+
+
+
+
Added in v0.18.0
+
+ Multilingual dictation on Linux
+
+
+ Per-language shortcuts and Follow keyboard layout arrived in v0.18.0.
+ If you write in more than one language, Vocalinux gives you three ways
+ to get the right one each time: auto-detect per utterance, a separate
+ shortcut per language, or following whichever keyboard layout you are
+ typing with. This page covers each, which engines support them, and
+ where they fall short. For a per-language view of engine support, see
+ the languages page.
+
+
+
+
+
+
+
Auto-detect
+
+ The engine guesses the language for each utterance. Easiest to set
+ up, but short clips can be guessed wrong.
+
+
+
+
+
Language shortcuts
+
+ A dedicated key per language. Explicit and reliable, for one
+ dictation at a time.
+
+
+
+
+
Follow keyboard layout
+
+ Switch layout to switch dictation language. Best on GNOME, where the
+ active layout can be read.
+
+
+
+
+
+
+ Choosing a main language
+
+
+ Settings → Speech Model opens with a simple Language group. Pick your
+ Main language from the searchable list. Turning on Other languages
+ makes the engine guess the language each time; the optional Other
+ language picker just records which one you had in mind, because
+ Whisper-family engines take a single language or none. On first run,
+ Vocalinux seeds the language from your keyboard layout and locale
+ (since v0.17.0).
+
+
+ Picking a language other than English switches off an English-only
+ model. From the command line, --language accepts{" "}
+ auto or any catalog code below.
+
+
+
+
+
+ Per-language dictation shortcuts
+
+
+ {howToSteps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
+ Bindings take effect immediately. A binding can also point at
+ Auto-detect. If two rows use the same key, the first one wins. More on
+ shortcut syntax and modes is on the{" "}
+ shortcuts page.
+
+
+
+
+
+ Following the keyboard layout
+
+
+ Expand Advanced on the Speech Model page and turn on Follow keyboard
+ layout. Vocalinux then re-reads the active layout at the start of
+ every dictation and uses the matching language. The Language picker
+ greys out and shows what the current layout resolves to; turning the
+ switch off pins that language.
+
+
+
+ Available for whisper.cpp, Whisper and Faster Whisper with a
+ multilingual model, and for the Remote API.
+
+
+ The active layout is read from GNOME's input-source settings.
+ Some IBus input methods map too, for example Mozc and Anthy to
+ Japanese, Pinyin and Rime to Chinese, and Hangul to Korean.
+
+
+ On other desktops Vocalinux uses the configured primary layout,
+ which does not follow your switches. A layout with no catalog
+ language falls back to auto-detect.
+
+
+ A language shortcut wins over the layout for that one dictation.
+
+
+
+
+
+
+ The settings in config.json
+
+
+ Settings writes these to ~/.config/vocalinux/config.json.
+ The value "layout" is how Follow keyboard
+ layout is stored:
+
+ On whisper.cpp, Settings → Advanced (after Unlock Advanced Settings)
+ also has Language Candidates: a comma-separated list such as{" "}
+ en,es that restricts auto-detect to those languages.
+ Leave it blank to consider all languages.
+
+
+
+
+
+ Engine caveats
+
+
+
+
+
+
Engine
+
Language picker
+
Follow layout
+
Language shortcuts
+
Notes
+
+
+
+ {engineRows.map((row) => (
+
+
{row.engine}
+
+ {row.picker}
+
+
+ {row.layout}
+
+
+ {row.shortcuts}
+
+
{row.note}
+
+ ))}
+
+
+
+
+ VOSK lists only languages that have an official Alphacephei model, 21
+ of the 33. Parakeet's v3-european model covers 25 European
+ languages and ignores the picker; see{" "}
+ Parakeet on Linux. For Whisper model
+ sizes, see the{" "}
+ Whisper model guide. The
+ Remote API sends your audio to the endpoint you configured; details
+ are on the Remote API page.
+
+
+
+
+
+ The language catalog
+
+
+ 33 languages plus Auto-detect, shared by Settings, language shortcuts
+ and the CLI. Slovenian was added in v0.18.4.
+
+ Install Vocalinux, keep the default whisper.cpp engine with a
+ multilingual model, and add a shortcut for each language you write in.
+ Names and jargon in any language can go in the{" "}
+ custom dictionary.
+
@@ -1035,12 +1039,12 @@ export default function HomePage() {
iPhone + Android
- Android beta / iOS TestFlight
+ Android on Google Play / iOS TestFlight
VocaPhone
- Android beta and iOS TestFlight. On-device first, gateway
- optional.
+ Android on Google Play and iOS TestFlight. On-device first,
+ gateway optional.
+ To start once with Parakeet from a terminal, run{" "}
+
+ vocalinux --engine parakeet
+
+ . In{" "}
+
+ ~/.config/vocalinux/config.json
+ {" "}
+ the engine name is{" "}
+ parakeet.
+
+
+
+
+
+ Parakeet models and Orukeet
+
+
+ The Model size picker lists three models for the Parakeet engine.
+ Sizes are the catalog values Vocalinux shows before a download.
+
+
+
+
+
+
Model ID
+
Model
+
Languages
+
Download
+
Notes
+
+
+
+ {models.map((model) => (
+
+
{model.id}
+
{model.name}
+
+ {model.languages}
+
+
+ {model.size}
+
+
{model.notes}
+
+ ))}
+
+
+
+
+ About the optional Orukeet model
+
+
+ Orukeet v0.1.0 was added in v0.18.0. It uses the same local
+ sherpa-onnx recognizer and covers 25 European languages; the
+ recommended Parakeet model and the default whisper.cpp engine are
+ unchanged. Vocalinux downloads it from a pinned revision of{" "}
+
+ oruk/orukeet on Hugging Face
+
+ , checks every file against bundled SHA-256 pins, and checks the
+ publisher's release manifest against those same pins. Its CC
+ BY-SA 4.0 weights license and NVIDIA attribution are saved with the
+ model.
+
+
+
+
+
Languages
+
+
+ With Parakeet, the model decides the language, not the Language
+ picker. Vocalinux treats the language as auto for this engine, so a
+ language you chose for whisper.cpp is not passed to Parakeet. Choose
+ v2-english if you only speak English, or v3-european (or Orukeet)
+ for the 25-language European set.
+
+
+ Two related features do not apply to Parakeet:{" "}
+ Follow keyboard layout works with whisper.cpp,
+ Whisper, Faster Whisper and the Remote API only, and custom
+ dictionary terms bias only the Whisper-family engines.
+ Transcript corrections from the custom dictionary do run after
+ Parakeet. See{" "}
+
+ languages by engine
+
+ ,{" "}
+
+ multilingual dictation
+ {" "}
+ and{" "}
+
+ custom dictionary
+
+ .
+
+
+
+
+
+
+ Hardware and packages
+
+
+ Parakeet runs on the CPU. Vocalinux's Vulkan and CUDA
+ acceleration applies to whisper.cpp, so a GPU does not speed up
+ Parakeet (see{" "}
+
+ GPU acceleration
+
+ ). Plan for roughly 640 MB of disk per Parakeet model, stored under{" "}
+
+ ~/.local/share/vocalinux/models/
+
+ .
+
+ Both are local engines. The differences are about languages, hardware
+ and packaging rather than a single winner. For every engine side by
+ side, see the{" "}
+
+ engine comparison
+
+ ; for whisper.cpp sizes, see the{" "}
+
+ Whisper model guide
+
+ .
+
+
+
+
+
+
+ Topic
+
+
Parakeet
+
whisper.cpp
+
+
+
+ {comparison.map((row) => (
+
+
+ {row.topic}
+
+
+ {row.parakeet}
+
+
+ {row.whisperCpp}
+
+
+ ))}
+
+
+
+
+ Pick Parakeet if you dictate in English or a supported European
+ language on a CPU-only machine and installed with install.sh, from
+ source or from the AUR. Stay on whisper.cpp if you use a language
+ outside that set, want GPU acceleration, rely on custom dictionary
+ terms, or installed the AppImage, Flatpak, Snap, .deb or .rpm.
+
+
+
+
+
+ Troubleshooting
+
+
+
+
+ Parakeet is missing from the Engine list
+
+
+ Settings lists only engines whose libraries are installed.
+ Parakeet appears once sherpa-onnx can be imported. Re-run the
+ installer with{" "}
+ --engine=parakeet.
+ Installs of this engine through install.sh were broken before
+ v0.18.4, so use a current installer; on Arch, install
+ python-sherpa-onnx.
+
+
+
+
+ The model will not load after an interrupted download
+
+
+ Vocalinux refuses to load Parakeet files that fail checksum
+ verification. In Settings → Speech Model, delete the leftover
+ model from Unused downloads and download it
+ again.
+
+
+
+
+ Text comes out in the wrong language
+
+
+ The Language picker does not steer Parakeet. If you only dictate
+ English, switch the model to v2-english. For a language outside
+ the European set, use whisper.cpp.
+
+
+
+
+ Anything else
+
+
+ Run{" "}
+ vocalinux --debug{" "}
+ and check{" "}
+
+ ~/.local/share/vocalinux/vocalinux.log
+
+ , then see the{" "}
+
+ troubleshooting guide
+
+ .
+
+
+
+
+
+
+
+ Parakeet FAQ
+
+
+ {faqs.map((faq) => (
+
+
{faq.question}
+
{faq.answer}
+
+ ))}
+
+
+
+
+
+ Try Parakeet on your machine
+
+
+ Install Vocalinux with the Parakeet engine, or start with whisper.cpp
+ and compare. Also see{" "}
+
+ Faster Whisper on Linux
+
+ , the other CPU engine added in v0.17.0.
+
+
+
+ Install Vocalinux
+
+
+
+ Compare engines
+
+
+
+
+ );
+}
diff --git a/web/src/app/pipewire/page.tsx b/web/src/app/pipewire/page.tsx
new file mode 100644
index 0000000..e2345af
--- /dev/null
+++ b/web/src/app/pipewire/page.tsx
@@ -0,0 +1,446 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import type { ReactNode } from "react";
+import {
+ AlertTriangle,
+ ChevronRight,
+ Headphones,
+ Mic,
+ Volume2,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { TerminalBlock } from "@/components/terminal-block";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/pipewire";
+const PAGE_TITLE = "PipeWire Audio Capture for Linux Voice Dictation";
+const PAGE_DESCRIPTION =
+ "How Vocalinux records from microphones and PipeWire system-audio sources, how it differs from PortAudio, and how to fix wrong-mic, silence and Bluetooth issues.";
+
+const captureRows = [
+ {
+ path: "Microphones (PortAudio)",
+ lists:
+ "Input-capable ALSA and PulseAudio devices that PortAudio enumerates, plus System Default",
+ records: "A PortAudio stream, downmixed to mono for the speech engine",
+ use: "Normal dictation into a mic or headset",
+ },
+ {
+ path: "System audio (PipeWire)",
+ lists:
+ "Every PipeWire Audio/Sink node, physical or virtual, shown as “System audio: …”",
+ records: "pw-record on that sink's monitor, raw 16-bit mono at 16 kHz",
+ use: "Transcribing what is playing through an output device",
+ },
+];
+
+const steps = [
+ {
+ name: "Open the Audio page",
+ text: "Open Vocalinux Settings, go to Audio and find Input Device. Use the refresh button next to the list after plugging in a headset.",
+ },
+ {
+ name: "Pick a source",
+ text: "Choose System Default to follow your desktop sound settings, a named microphone to pin that device, or a System audio entry to record what plays through that output.",
+ },
+ {
+ name: "Run the Microphone Test",
+ text: "Click Test next to Microphone Test. It records 2 seconds and the level meter shows whether any signal arrived.",
+ },
+ {
+ name: "Try a real dictation",
+ text: "Use Test Dictation in the Settings sidebar footer, or your dictation shortcut, to confirm the engine hears speech from the chosen source.",
+ },
+];
+
+const faqs = [
+ {
+ question: "Does Vocalinux record microphones through PipeWire?",
+ answer:
+ "Microphones are listed and opened through PortAudio, including System Default. The native PipeWire path, pw-dump plus pw-record, is used for the System audio entries, because PortAudio cannot reach a sink monitor.",
+ },
+ {
+ question: "Can I capture audio from one application only?",
+ answer:
+ "No. A System audio entry records the monitor of a whole output device, so everything playing through that sink is mixed into the recording. Per-application stream capture is out of scope in the current code.",
+ },
+ {
+ question: "Why are there no System audio entries in my device list?",
+ answer:
+ "Vocalinux lists them only when both pw-dump and pw-record are on the PATH and the PipeWire daemon answers. If either tool is missing, the list shows microphones only and nothing else changes.",
+ },
+ {
+ question: "Does system-audio capture send anything online?",
+ answer:
+ "Not with a local engine. Audio from either path goes to the engine you selected, and local engines run on your machine. If you switch to the Remote API engine or a VocaGateway, audio is sent to the endpoint you configured.",
+ },
+ {
+ question: "Which version added PipeWire capture?",
+ answer:
+ "PipeWire system-audio capture was added in v0.18.0. Check your version with vocalinux --version.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: `${PAGE_TITLE} | Vocalinux`,
+ description: PAGE_DESCRIPTION,
+ path: PAGE_PATH,
+ keywords: [
+ "pipewire voice dictation linux",
+ "pipewire speech to text",
+ "linux dictation system audio",
+ "pw-record transcription",
+ "portaudio vs pipewire",
+ "vocalinux microphone not working",
+ "bluetooth headset dictation linux",
+ ],
+});
+
+function InlineCode({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export default function PipeWirePage() {
+ const jsonLd = {
+ "@context": "https://schema.org",
+ "@graph": [
+ {
+ "@type": "TechArticle",
+ headline: PAGE_TITLE,
+ description: PAGE_DESCRIPTION,
+ dateModified: "2026-11-01",
+ author: {
+ "@type": "Person",
+ name: "Jatin K Malik",
+ url: "https://github.com/jatinkrmalik",
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "Vocalinux",
+ logo: {
+ "@type": "ImageObject",
+ url: absoluteUrl("/vocalinux.png"),
+ },
+ },
+ mainEntityOfPage: absoluteUrl(PAGE_PATH),
+ },
+ {
+ "@type": "HowTo",
+ name: "Choose an audio source for Vocalinux dictation",
+ step: steps.map((step, index) => ({
+ "@type": "HowToStep",
+ position: index + 1,
+ name: step.name,
+ text: step.text,
+ })),
+ },
+ {
+ "@type": "FAQPage",
+ mainEntity: faqs.map((faq) => ({
+ "@type": "Question",
+ name: faq.question,
+ acceptedAnswer: { "@type": "Answer", text: faq.answer },
+ })),
+ },
+ ],
+ };
+
+ return (
+
+
+
+
+
Added in v0.18.0
+
+ PipeWire audio capture in Vocalinux
+
+
+ Vocalinux records from two kinds of source: microphones, opened
+ through PortAudio, and system-audio sources that come straight from
+ PipeWire. This page explains what each one is for, how to pick the
+ right source, and what to check when dictation hears the wrong device
+ or nothing at all.
+
+
+
+
+
+ Two capture paths: PortAudio and PipeWire
+
+
+ PortAudio enumerates ALSA and PulseAudio devices, not PipeWire nodes.
+ That works well for microphones, but it cannot see a sink's
+ monitor, the stream of everything playing through an output. Since
+ v0.18.0 Vocalinux asks PipeWire directly: it lists sinks with{" "}
+ pw-dump and records the chosen one with{" "}
+ pw-record --raw. PipeWire converts the audio
+ to the format Vocalinux asks for inside its own graph, so no format
+ negotiation is needed.
+
+
+
+
+
+
+ Path
+
+
+ What it lists
+
+
+ How it records
+
+
+ Use it for
+
+
+
+
+ {captureRows.map((row) => (
+
+
+ {row.path}
+
+
+ {row.lists}
+
+
+ {row.records}
+
+
{row.use}
+
+ ))}
+
+
+
+
Who this is for
+
+ Most people dictate into a microphone and never need to change
+ anything. System audio is for the cases a microphone cannot cover
+ well, such as transcribing speech from a video or a call that plays
+ through your speakers or headphones. For recorded files, the{" "}
+
+ file transcription mode
+ {" "}
+ is usually the better tool.
+
+
+
+
+
+ How to choose a source
+
+
+ {steps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
+
+
+
System Default
+
+ Leaves valid default and virtual inputs under the sound
+ server's control, so the mic you pick in desktop sound
+ settings is the one Vocalinux hears.
+
+
+
+
+
A named microphone
+
+ A valid explicit choice always wins over System Default. Vocalinux
+ saves the device name and matches it again by name when indexes
+ shift after a replug.
+
+
+
+
+
System audio: …
+
+ Records one output device's monitor. Two sinks with the same
+ description get the PipeWire node name added in brackets so you
+ can tell them apart.
+
+
+
+
+
+
+
+ Settings and limits
+
+
Where the choice is stored
+
+ The selection lives in{" "}
+ ~/.config/vocalinux/config.json under{" "}
+ audio.device_index and{" "}
+ audio.device_name. A{" "}
+ null index means System Default.
+
+
Gotchas
+
+
+ System audio needs both pw-dump and{" "}
+ pw-record. Without them the entries are
+ simply not listed.
+
+
+ If a saved System audio sink disappears, Vocalinux refuses to record
+ instead of quietly switching to a microphone, because that would
+ capture the wrong audio. Pick a source again.
+
+
+ A sink monitor records the full mix for that output, including
+ notification sounds and any other app playing at the time.
+
+
+ The Flatpak release notes list PipeWire system-audio capture as a
+ native-install path. Inside the Snap, Settings → Audio lists the
+ sound server's microphones through{" "}
+ pactl instead.
+
+
+
+
+
+
+
+ Troubleshooting
+
+
+ Dictation uses the wrong microphone
+
+
+ Select the mic by name in Settings → Audio → Input Device rather than
+ relying on System Default. On ALSA-only setups where PortAudio
+ substitutes some other hardware for the default input, Vocalinux
+ prefers an input-capable pulse device so it
+ follows your desktop's choice, and logs a warning when none
+ exists. Run with debug logging to see which device was opened:
+
+
+
+
+
+ Silence, or a Microphone Stalled notification
+
+
+ Check mute state and levels in your system sound settings, then list
+ capture hardware:
+
+
+
+
+
+ A Microphone Stalled notification means the selected input stopped
+ delivering audio and Vocalinux stopped that attempt. Use Microphone
+ Test, or choose another Input Device. If a stale device index is the
+ problem, quit Vocalinux and set{" "}
+ audio.device_index to{" "}
+ null in config.json.
+ With a System audio source, remember that a silent output produces
+ silence: start the playback first.
+
+
Bluetooth headsets
+
+ Bluetooth headset microphones use a separate capture profile that is
+ sensitive to repeated opening. Vocalinux opens the stream once per
+ session and pauses briefly between failed attempts on Bluetooth
+ devices. If capture still fails, try a wired microphone to check
+ whether the Bluetooth capture path is the cause.
+
+
+
+
+
FAQ
+
+ {faqs.map((faq) => (
+
+
{faq.question}
+
{faq.answer}
+
+ ))}
+
+
+ More fixes on the{" "}
+
+ troubleshooting page
+
+ . How audio is handled with local and remote engines is covered in{" "}
+
+ offline dictation
+ {" "}
+ and{" "}
+
+ Remote API
+
+ . Speech detection itself is explained in{" "}
+
+ voice activity detection
+
+ .
+
+
+
+
+
+ Pick a source and dictate
+
+
+ Install Vocalinux, choose your microphone or a system-audio source in
+ Settings → Audio, and run the Microphone Test before your first
+ dictation.
+
+
+
+ );
+}
diff --git a/web/src/app/transcribe-audio-files/page.tsx b/web/src/app/transcribe-audio-files/page.tsx
new file mode 100644
index 0000000..8e5d2b6
--- /dev/null
+++ b/web/src/app/transcribe-audio-files/page.tsx
@@ -0,0 +1,407 @@
+import Link from "next/link";
+import { type Metadata } from "next";
+import {
+ CheckCircle2,
+ ChevronRight,
+ Lock,
+ Mic,
+ Terminal,
+ Users,
+} from "lucide-react";
+import { SeoSubpageShell } from "@/components/seo-subpage-shell";
+import { TerminalBlock } from "@/components/terminal-block";
+import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+
+const PAGE_PATH = "/transcribe-audio-files";
+
+const cliCommand = "vocalinux --transcribe-file meeting.wav";
+
+const howToSteps = [
+ {
+ name: "Open the tray entry",
+ text: "Click the Vocalinux tray icon and choose Transcribe Audio File…",
+ },
+ {
+ name: "Pick a recording",
+ text: "Select a WAV file, or an MP3, OGG, FLAC, M4A or Opus file if ffmpeg is installed, and click Open.",
+ },
+ {
+ name: "Download TinyDiarize once",
+ text: "The first time, Vocalinux asks to download the TinyDiarize model (about 465 MB, one time). Click Download. It is only used for file transcription and never becomes your dictation model.",
+ },
+ {
+ name: "Wait for the transcript",
+ text: "A notification says the file is being transcribed. Long files can take a while. When it finishes, a Transcript window opens with timestamps and speaker labels.",
+ },
+ {
+ name: "Copy or save it",
+ text: "Use Copy to put the full transcript on the clipboard, or Save As to write it to a text file (transcript.txt by default).",
+ },
+];
+
+const formatRows = [
+ {
+ input: "WAV, 16-bit, 16 kHz, mono or stereo",
+ path: "Read directly. No extra tools needed.",
+ },
+ {
+ input: "Any other WAV",
+ path: "Decoded with ffmpeg to mono 16 kHz first.",
+ },
+ {
+ input: "MP3, OGG, FLAC, M4A, Opus",
+ path: "Offered in the tray file picker when ffmpeg is installed. Decoded with ffmpeg.",
+ },
+ {
+ input: "Other files (CLI)",
+ path: "Handed to ffmpeg, so anything your ffmpeg build can decode.",
+ },
+];
+
+const faqs = [
+ {
+ q: "Can it label speakers in languages other than English?",
+ a: "No. The TinyDiarize model Vocalinux uses, small.en-tdrz, is English-only. Speaker labels and the transcript itself are for English audio.",
+ },
+ {
+ q: "Does Speaker 1 always mean the same person?",
+ a: "No. TinyDiarize marks the points where the speaker changes, and Vocalinux numbers each new turn in order. A person who speaks again later gets a new number, so treat the labels as turn markers and rename them yourself.",
+ },
+ {
+ q: "Why does the CLI say the model is not downloaded?",
+ a: "vocalinux --transcribe-file never downloads anything. Run Transcribe Audio File… from the tray once and accept the TinyDiarize download, then the CLI can use the model.",
+ },
+ {
+ q: "Is my audio uploaded anywhere?",
+ a: "No. The file is decoded and transcribed on your machine with whisper.cpp, even if your dictation engine is set to a remote API. The only network use is the one-time model download.",
+ },
+ {
+ q: "Why can I only pick WAV files in the tray?",
+ a: "ffmpeg is not installed or not on your PATH. Without it, the file picker only offers WAV. Install ffmpeg from your distribution's packages to enable MP3, OGG, FLAC, M4A and Opus.",
+ },
+];
+
+export const metadata: Metadata = buildPageMetadata({
+ title: "Transcribe Audio Files on Linux Offline with Speakers | Vocalinux",
+ description:
+ "Transcribe an audio file on Linux offline with Vocalinux: tray or --transcribe-file, local TinyDiarize speaker labels (English), formats, output and limits.",
+ path: PAGE_PATH,
+ keywords: [
+ "transcribe audio file Linux offline",
+ "speaker diarization Linux",
+ "TinyDiarize",
+ "offline meeting transcription Linux",
+ "whisper.cpp speaker labels",
+ "transcribe WAV MP3 Linux",
+ ],
+});
+
+export default function TranscribeAudioFilesPage() {
+ const articleJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "TechArticle",
+ headline: "Transcribe Audio Files on Linux Offline with Speaker Labels",
+ description:
+ "How to transcribe recordings locally with Vocalinux from the tray or the --transcribe-file CLI, and how TinyDiarize speaker labels work.",
+ dateModified: "2026-11-01",
+ author: {
+ "@type": "Person",
+ name: "Jatin K Malik",
+ url: "https://github.com/jatinkrmalik",
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "Vocalinux",
+ logo: {
+ "@type": "ImageObject",
+ url: absoluteUrl("/vocalinux.png"),
+ },
+ },
+ mainEntityOfPage: absoluteUrl(PAGE_PATH),
+ };
+
+ const howToJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "HowTo",
+ name: "Transcribe an audio file with speaker labels in Vocalinux",
+ step: howToSteps.map((step, index) => ({
+ "@type": "HowToStep",
+ position: index + 1,
+ name: step.name,
+ text: step.text,
+ })),
+ };
+
+ const faqJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: faqs.map((item) => ({
+ "@type": "Question",
+ name: item.q,
+ acceptedAnswer: { "@type": "Answer", text: item.a },
+ })),
+ };
+
+ return (
+
+
+
+
+
+
+
Added in v0.18.0
+
+ Transcribe audio files offline, with speaker labels
+
+
+ Vocalinux can turn a recorded meeting, interview or voice memo into a
+ text transcript on your own machine. It uses TinyDiarize, a
+ whisper.cpp model that also marks where the speaker changes, so the
+ transcript comes out as timestamped blocks labelled Speaker 1, Speaker
+ 2 and so on. Start it from the tray or from a terminal.
+
+
+
+
+
+
+
+ For recordings, not live dictation
+
+
+ File transcription is separate from the dictation pipeline. It never
+ types into other apps and does not change your dictation model.
+
+
+
+
+
Speaker turns, English only
+
+ The model is small.en-tdrz. It is English-only, so use
+ it for English recordings.
+
+
+
+
+
Local processing
+
+ Audio is decoded and transcribed on your computer. Nothing is sent
+ to a server.
+
+
+
+
+
+
+ From the tray
+
+
+ {howToSteps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
{step.name}
+
+
{step.text}
+
+ ))}
+
+
+ The model download is checked against a pinned SHA-256 digest before
+ use, like every other Vocalinux model. The status line under the
+ transcript reports how many blocks were found and the highest speaker
+ number.
+
+
+
+
+
+
+ From the command line
+
+
+ --transcribe-file PATH transcribes one file, prints the
+ diarized transcript to standard output and exits:
+
+
+
+
+ It needs no display and no tray, and it runs alongside a Vocalinux
+ instance that is already running, so it works over SSH or from a
+ script.
+
+
+ It does not download the model. Fetch TinyDiarize once through the
+ tray entry first, otherwise the command stops with a message saying
+ the model is not downloaded yet.
+
+
+ Because the transcript goes to standard output, you can redirect it
+ to a file with your shell. Errors go to standard error, prefixed
+ with vocalinux: could not transcribe, and the exit
+ status is 1. A file with no detectable speech prints{" "}
+ vocalinux: no speech detected.
+
+
+
+
+
+
+ Supported input formats
+
+
+
+
+
+
Input
+
How Vocalinux reads it
+
+
+
+ {formatRows.map((row) => (
+
+
{row.input}
+
{row.path}
+
+ ))}
+
+
+
+
+ Vocalinux does not install ffmpeg for you. Without it, only 16-bit 16
+ kHz WAV files can be transcribed, and the tray picker's filter
+ says so.
+
+
+
+
+
+ Output format
+
+
+ Each block is one speaker turn: a [HH:MM:SS] timestamp
+ for where it starts, the speaker label, then the text. Blocks are
+ separated by a blank line. The Transcript window, Copy, Save As and
+ the CLI all use the same layout. The lines below show the shape only;
+ they are not real output.
+
+ TinyDiarize is a small, English-only whisper.cpp model whose output
+ includes a speaker-turn marker wherever it detects a change of voice.
+ Vocalinux runs it in whisper.cpp's tinydiarize mode and splits
+ the text at each marker. Each new block gets the next number.
+
+
+ That makes the labels positional, not identities. In a two-person
+ interview the transcript reads Speaker 1, Speaker 2, Speaker 3,
+ Speaker 4 as the two people take turns, rather than alternating
+ between 1 and 2. Turn boundaries are useful for reading and editing;
+ assigning names is up to you.
+
+
+
+
+
+ Privacy and limitations
+
+
+ {[
+ "Local: ffmpeg decoding and whisper.cpp transcription both run on your machine. File transcription always uses the local TinyDiarize model, even when dictation is set to the Remote API engine.",
+ "Network: only the one-time model download, from a pinned upstream revision.",
+ "Storage: the transcript is not written to disk unless you click Save As or redirect the CLI output.",
+ "English only, and speaker numbers do not identify people.",
+ "The model is about 465 MB, so the first run needs that much free disk space plus download time.",
+ "Long files take a while. The tray shows a notification while it works.",
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+ More on what stays on your machine:{" "}
+ offline dictation and the{" "}
+ privacy policy.
+
+
+
+
+
FAQ
+
+ {faqs.map((item) => (
+
+
{item.q}
+
{item.a}
+
+ ))}
+
+
+
+
+
+ Transcribe your first file
+
+
+ File transcription is available from v0.18.0; check yours with{" "}
+ vocalinux --version. For live dictation model choices,
+ see the Whisper model guide;
+ to fix names the model mishears in dictation, use the{" "}
+ custom dictionary.
+