Vocalinux

vocalinux.com
Vocalinux

Offline system-wide voice dictation for Linux (X11 and Wayland). Uses local models (whisper.cpp, Whisper, or VOSK) so microphone audio never leaves the device. Tray app with hotkeys; GPLv3. Currently beta software.

Open Source

Vocalinux Source Code

Author

jatinkrmalik

Description

Free, open-source, 100% offline voice dictation for Linux. Speak and type anywhere via whisper.cpp, Whisper & VOSK engines, GPU-accelerated, works on X11 + Wayland!

#accessibility#dictation#gpu-acceleration#linux#offline-first#privacy-first#python#speech-recognition#speech-to-text#voice#voice-typing#vosk#wayland#whisper#whisper-cpp

Homepage

https://vocalinux.com/

License

GPL-3.0

Created

12 Apr 25

Last Updated

23 Jul 26

Latest version

v0.14.2

Primary Language

Python

Size

20,806 KB

Stars

652

Forks

68

Watchers

652

Language Usage

Language Usage

Star History

Star History

Recent Commits

  • Anay Garodia (20 Jul 26)

    fix(vosk): add missing Italian and English-India entries to medium/large model tables (#551) Fixes #550. Root cause: VOSK_MODEL_INFO["medium"]["languages"] and VOSK_MODEL_INFO["large"]["languages"] in vosk_model_info.py were missing entries for "it" (Italian) and "en-in" (English-India), even though both languages are present in the "small" table and VOSK actually publishes bigger models for them (vosk-model-it-0.22, vosk-model-en-in-0.5 at alphacephei.com/vosk/models). When a user selected Italian + the medium (or large) VOSK model, _init_vosk() built vosk_model_map["medium"] = None (via .get() on a missing key), and _get_vosk_model_path() then called os.path.join(MODELS_DIR, None), crashing with: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' before any download was even attempted. This matches the exact debug log in the issue and the settings-dialog download flow the reporter exercised. Fix: - Add the missing "it"/"en-in" entries to the medium/large tables so all 10 supported languages now resolve consistently across all three sizes. - Add a guard in _get_vosk_model_path() that raises a clear ValueError ("No VOSK '<size>' model available for language '<lang>'...") instead of crashing deep inside os.path.join, so any future data gap fails loudly and helpfully rather than with a cryptic TypeError.

  • Jatin K Malik (18 Jul 26)

    docs(readme): redistribute header badges into three rows (#545) Four badge rows felt too tall above the hero. Collapse to three rows (5 / 6 / 5) while keeping the narrow-to-wide step-out layout.

  • Jatin K Malik (18 Jul 26)

    docs(readme): pin CI badge to main and restyle social/coverage badges (#544) * docs(readme): pin CI badge to main and restyle social/coverage badges Scope the Vocalinux CI status badge to the main branch so PR failures no longer flip the README shield. Restyle the X follow badge to black/white X aesthetics, and set Codecov coverage.range so 80%+ coverage shows green. * docs(readme): use static X follow badge without broken count Shields.io no longer returns Twitter/X follower counts (empty right-side value after API restrictions). Switch to a static black badge that shows Follow @jatinkrmalik with the X logo instead of an empty dynamic count.

  • Jatin K Malik (18 Jul 26)

    docs(readme): center-align header before hero image (#542) * docs(readme): center-align header before hero image Wrap icon, title, subtitle, and shields in a centered div so the top of the README lines up cleanly above the hero image. * docs(readme): tighten header badges and add social/install shields Three clear rows: identity (Stable, release, license, PyPI, AUR), quality (CI, Python, Linux, codecov), and community (stars, forks, issues, contributions, X follow). Drop low-signal noise (watchers, commit activity, last commit, Made with GTK). Slightly larger logo. * docs(readme): add privacy, distro, and PRs-welcome shields Expand the header into five scannable rows: identity, product values (privacy/offline, data stays local, X11|Wayland, PRs welcome), official distro logos linking to DISTRO_COMPATIBILITY, quality, and community. Drop the generic Platform: Linux badge in favor of named distros. * docs(readme): trim header to four badge rows Drop Stable (release version is enough), merge privacy into one badge, and remove stars/forks/issues (already on the GitHub repo page). Keep X follow with CI/Python/codecov on the last row. * docs(readme): order badge rows narrowest to widest Step the header out toward the full-width hero: values → identity → quality/social → distros as the base row.

  • Jatin K Malik (18 Jul 26)

    chore(release): prepare v0.14.2 (#537) * chore(release): prepare v0.14.2 IBus stability patch on the 0.14 series: engine process launch after XDG import (#534) and FocusIn gate for first dictation on GNOME Wayland (#533). Keep 0.14 series feature list in README/UPDATE for patch releases; changelog and AppStream note only the delta. Document that rule in RELEASE_PROCESS. * fix(release): address 0.14.2 review findings - Dates: v0.14.2 uses 2026-07-17 (not before 0.14.1) - AUR: sha256sums=SKIP until tag; document updpkgsums in RELEASE_PROCESS - Single version source: package __init__ re-exports version.py; User-Agent uses __version__ - Docs: drop redundant series recaps; shrink AppStream/changelog delta - README ecosystem status: Stable v0.14.2 (not Beta) * docs(release): include settings scroll fixes in 0.14.2 notes Cover #538 and #541 (scrollable settings tabs / wheel polish) in README, UPDATE, website changelog, AppStream, and version history after rebasing onto main.

  • Jatin K Malik (18 Jul 26)

    fix(ui): polish settings tab scrolling after #538 (#541) * fix(ui): polish settings tab scrolling after oversized-dialog fix Forward wheel events from unfocused combos/spins to the tab scroller, drop the nested Advanced ScrolledWindow, and clear tab scroller shadows. * refactor(ui): re-dispatch scroll to parent ScrolledWindow Drop hand-rolled vadjustment math; forward the event and let GTK scroll.

  • Black Eyed Hat Man (18 Jul 26)

    fix(ui): make settings tabs scrollable so dialog fits the monitor (#538) The settings dialog opened taller than the screen (spilling onto a second monitor) because none of the notebook tabs scrolled. A Gtk.Notebook requests the minimum size of its tallest page, and the Speech Engine (~950px) and Recognition (~1080px) tabs pushed the window's minimum height to ~1350px at 700px width, overriding set_default_size() entirely on 1080p displays. Wrap each tab in a vertical-only Gtk.ScrolledWindow so the notebook's minimum height collapses and the default size takes effect, and raise the default height from min(600, 40% of screen) to min(800, 75%) since tall content now scrolls instead of stretching the window. Measured before/after on a 1920x1080 monitor: opens at 700x1351 before, 700x800 after (window minimum drops from 684x1161 to 684x243). Co-authored-by: Claude Fable 5 <[email protected]>

  • Jatin K Malik (17 Jul 26)

    fix(ibus): wait for FocusIn before commit on scoped injection (#533) * fix(ibus): wait for FocusIn before commit on scoped injection Cold first activation of the vocalinux engine on GNOME Wayland could commit before mutter bound a client context, so logs reported success while the first dictation of a session was dropped. Fixes #523 * refactor(ibus): clear focus event directly in do_destroy Drop the clear_focus callback on the destroy helper; one local branch is enough. * test(ibus): cover FocusIn gate helpers for patch coverage Extract PING/focus-wait helpers and exercise them so codecov/patch clears the 80% target on the #523 fix.

  • Jatin K Malik (17 Jul 26)

    fix(ibus): restore engine process launch after XDG path import (#534) * fix(ibus): restore engine process launch after XDG path import PR #484 switched ibus_engine to a relative import of xdg_data_home for Flatpak-safe paths. start_engine_process and the IBus component exec still run this file by path (python /path/to/ibus_engine.py), which has no package context, so the process died immediately with ImportError and Vocalinux fell back to ydotool (clipboard paste). Accept absolute/inline import fallbacks when loaded as a script, log child stderr from engine.stderr.log on startup failure, and add a path-based --xml smoke test so this does not regress. * test(ibus): cover start_engine_process failure diagnostics Raise patch coverage for the engine.stderr.log failure paths so codecov/patch meets the 80% target on #534. * chore: re-trigger CI after rebase onto main

  • Jatin K Malik (17 Jul 26)

    ci(flatpak): build arch matrix without job-level matrix if (#536) GitHub rejects job-level if expressions that reference matrix (Unrecognized named-value: matrix). Resolve the include list in a small setup job instead so PRs stay x86_64-only.

  • Jatin K Malik (17 Jul 26)

    ci(flatpak): reuse builder cache and skip aarch64 on PRs (#535) Using github.sha in the cache key forced a full rebuild on every commit. Hash packaging/flatpak instead so .flatpak-builder can restore across runs, and only rebuild modules that actually changed. PRs build x86_64 only; main and workflow_dispatch still cover both arches. Drop the extra upload-artifact step in favor of the action's own upload.

  • Jatin K Malik (17 Jul 26)

    chore(release): prepare v0.14.1 (stable for Flathub) (#532) Drop beta suffix so Flathub stable AppStream lint accepts the release.

  • Jatin K Malik (17 Jul 26)

    docs(web): add Flathub domain verification well-known file (#530) Empty org.flathub.VerifiedApps.txt under /.well-known/ so Flathub can verify com.vocalinux.Vocalinux against vocalinux.com after GitHub Pages deploy.

  • Jatin K Malik (17 Jul 26)

    chore(release): prepare v0.14.1-beta (#525) Patch release for Flatpak/AUR packaging and post-0.14.0 fixes: installer wrappers, ydotool dual API and Flatpak socket probe, model download timeouts, layout-aware hotkeys, and related docs/web bumps.

  • Jatin K Malik (16 Jul 26)

    feat(flatpak): add Flatpak packaging for universal distribution (#484) * feat(flatpak): add Flatpak packaging for universal distribution Adds a Flathub-ready Flatpak for the whisper.cpp engine, plus the minimal runtime adaptations Flatpak actually needs — deliberately scoped down from the earlier attempts (#416, #428), which bundled unrelated whisper.cpp GPU, audio, and clipboard changes. Packaging (packaging/flatpak/): - app manifest, AppStream metainfo, and desktop entry (com.vocalinux.Vocalinux) - generated offline Python dependency modules (flatpak-pip-generator) - bundled shared modules: PortAudio, libayatana-appindicator, xdotool, xsel Runtime changes (Flatpak-only, behaviour unchanged outside a sandbox): - utils/paths.py: honour XDG_CONFIG_HOME/XDG_DATA_HOME so config, models and runtime data live under ~/.var/app/<app-id> in the sandbox - autostart uses `flatpak run <app-id>` (the host launcher is always on PATH) - text injection and keyboard backend detect a Wayland host reached via XWayland (no Wayland socket granted) and route through xdotool / pynput-on-X11 - tray indicator references the exported `<app-id>-*` host icon theme names Also: a CI workflow building x86_64 + aarch64 bundles, and README / packaging README / distro-compatibility doc updates. Verified with a full flatpak-builder build + bundle install on aarch64: pywhispercpp loads its native libraries without LD_LIBRARY_PATH, XDG paths redirect correctly, bundled tools are present, and the entrypoint runs. Manifest and builddir lint are clean and AppStream metadata validates. Closes #167 * chore(flatpak): sync metainfo version to 0.14.0-beta Also ignore flatpak-build-dir/ so local builder output stays untracked. * refactor(flatpak): trim docs and comments after ponytail review Drop redundant packaging README command blocks, shrink path/tray/autostart comments, and stop ignoring a non-canonical build-dir name. * fix(flatpak): close readiness gaps for v0.14.0-beta Route log files through XDG data dirs so Flatpak keeps them under ~/.var/app, refresh AppStream screenshots to vocalinux.com, and use the SPDX GPL-3.0-only license id. * fix(flatpak): enable global hotkeys via evdev and --device=input pynput over XWayland only receives keys for the focused X client. Prefer evdev inside Flatpak and grant /dev/input so shortcuts work app-wide. * fix(flatpak): inject text with ydotool/uinput on Wayland xdotool only reaches XWayland clients, so typing appeared to succeed but never hit native apps. Ship ydotool, grant --device=all for uinput, start ydotoold on demand, and type via uinput inside the Flatpak. * fix(flatpak): paste via wl-copy + Ctrl+V instead of typing ydotool type injects character-by-character. Ship wl-clipboard, grant --socket=wayland, and prefer clipboard paste for instant insertion. * test(text-injection): expect ydotool default key-delay of 2 Matches the shipped VOCALINUX_YDOTOOL_KEY_DELAY default used when clipboard paste falls back to character typing.

  • Jatin K Malik (16 Jul 26)

    fix: sg not found on Ubuntu 26.04/Debian 13 (#524) * fix: sg not found on Ubuntu 26.04/Debian 13 The 'sg' command moved from the 'login' package to 'util-linux-extra' in Debian 13 and Ubuntu 26.04, which is not installed by default. This causes vocalinux and vocalinux-gui wrappers to fail with 'exec: sg: not found'. Two changes: 1. Add util-linux-extra to APT dependency lists (Ubuntu + Debian 13+) 2. Add command -v sg safety check in both wrapper scripts so they fall back to direct execution if sg isn't available Fixes #522 * fix: attempt to install util-linux-extra if sg is missing When the wrapper scripts detect that sg is not available, they now attempt to install util-linux-extra via apt-get before falling back to direct execution. This provides a better user experience on Ubuntu 26.04/Debian 13 where sg moved to a separate package. The installation runs silently and only triggers if: 1. User is in input group but session doesn't have it 2. sg command is not found 3. apt-get is available (Debian/Ubuntu systems) If installation fails or sg still isn't available, the scripts fall back to direct execution (app starts but keyboard shortcuts won't work until next login). * refactor: extract shared sg check, drop runtime install - Extract sg fallback logic to shared SG_CHECK variable (eliminates duplication) - Remove runtime apt-get install of util-linux-extra (dependency list handles it) - Flatten to simple command -v sg check with direct exec fallback net: -24 lines --------- Co-authored-by: Alfred Pennyworth <[email protected]>

  • Jatin K Malik (15 Jul 26)

    docs(screenshots): refresh v0.14 UI shots and add website gallery (#521) * docs(screenshots): refresh v0.14 settings/About shots and add website gallery Replace the outdated About dialog screenshot and supersede the old single settings dialog with the six v0.14.0-beta settings tabs. Keep unique product shots that have no replacements. Add a dedicated /screenshots/ website page with header/nav discoverability and a homepage link (no full gallery on home). * docs(screenshots): drop obsolete features-overview shot The annotated features overview was the pre-tabbed settings panel and no longer matches the v0.14 UI. Remove it from README, website gallery, and assets. * docs(screenshots): ship full-resolution web transcription shot Restore the original PNG instead of the downscaled copy so the website gallery matches the repo assets bit-for-bit. * feat(web): add click-to-expand lightbox on screenshots gallery Open a full-screen dialog when a shot is clicked, with prev/next controls, keyboard arrows, Escape to close, and captioned counter for browsing the set. * fix(web): avoid next/image HMR crash on screenshots gallery Use plain img tags in the client gallery so dev-mode webpack chunk reloads do not throw options.factory call errors while hydrating.

  • Jatin K Malik (15 Jul 26)

    feat(packaging): add AUR release package and CI publish (#518) * feat(packaging): add AUR release package and CI publish Ship packaging/aur/vocalinux for yay installs of tagged releases, wire AUR publish into the release workflow (skipped until secrets are set), and document one-time maintainer setup. Fixes #116 * refactor(packaging): slim AUR PR to PKGBUILD + CI Drop systemd unit, install script, website edits, and long docs. Keep release PKGBUILD, minimal publish job, and short AUR setup notes.

  • Jatin K Malik (15 Jul 26)

    fix(hotkeys): layout-aware combo keys for non-US layouts (#513) (#514) * fix(hotkeys): resolve combo letters via active XKB layout Settings capture stores character tokens from GDK keyvals (e.g. alt+a), but the Wayland/evdev path mapped letters to fixed US KEY_* codes. On AZERTY that made Alt+A listen for the key labeled Q. Resolve letter tokens through libxkbcommon for the active layout so recording and listening agree. Fixes #513 * refactor(hotkeys): slim layout-aware key resolution Drop unused resolve helper, dual knobs, setxkbmap/localectl probes, and redundant tests. Keep gsettings + libxkbcommon map and the AZERTY combo path. * test(hotkeys): drop redundant AZERTY resolve-only case Keep fire/no-fire combo coverage and the XKB fr map check. * test(hotkeys): cover layout map detection and error paths Raise patch coverage for layout_key_map / evdev_code_for_key so codecov patch target (80%) is met.

  • Jatin K Malik (15 Jul 26)

    fix(web): resolve Dependabot npm alerts in package-lock (#515) Bump brace-expansion, js-yaml, @babel/core, and related transitive deps via npm audit fix. Override nested postcss so Next.js no longer pulls vulnerable 8.4.31 (XSS in CSS stringify output).

  • Jatin K Malik (15 Jul 26)

    fix(text-injection): treat XIM none as unset (#512)

  • Jatin K Malik (14 Jul 26)

    Update README.md

  • Jatin K Malik (14 Jul 26)

    Enhance README with Star History section Added a responsive star history chart to the README.

  • dependabot[bot] (14 Jul 26)

    chore(deps-dev): bump ws (#510) Bumps the npm_and_yarn group with 1 update in the /web directory: [ws](https://github.com/websockets/ws). Updates `ws` from 8.19.0 to 8.21.0 - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/8.19.0...8.21.0) --- updated-dependencies: - dependency-name: ws dependency-version: 8.21.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • Jatin K Malik (14 Jul 26)

    chore(release): prepare v0.14.0-beta * chore(release): prepare v0.14.0-beta - Bump version 0.13.0-beta -> 0.14.0-beta (src/vocalinux/version.py) - README: refresh What's New for v0.14.0-beta and bump comparison table - docs/UPDATE.md: add v0.14.0-beta section and bump checkout example - SECURITY.md: mark 0.14.x supported, 0.13.x unsupported - docs/RELEASE_PROCESS.md: add v0.14.0-beta to version history - web: bump package.json / package-lock.json / page.tsx schema + badge - web changelog: add v0.14.0-beta entry * ci(codecov): allow 0.5% project coverage threshold Prevents version-bump/docs-only PRs from being blocked by minor coverage fluctuations (e.g. -0.04% on a release-prep branch). * docs(release): correct chronological order in version history table * docs(release): add #509 shortcut UI exclusivity fix to v0.14.0-beta notes

  • Jatin K Malik (14 Jul 26)

    fix(ui): keep preset and custom shortcut selection exclusive (#509) Selecting a preset after recording a custom combo left the custom entry filled, so both looked active. Sync the dropdown to "Custom Shortcut" when a non-preset is set, and clear the custom field when a preset wins.

  • Jatin K Malik (13 Jul 26)

    fix(text-injection): restore GNOME Wayland IBus with bare xkb engine (#506) #502 only exempted KDE from the #478 real-engine gate. GNOME/Mutter has the same scoped-activation path, so stock sessions with only an xkb:* layout fell through to wtype, which Mutter rejects, then clipboard-only. Allow any Wayland session without an explicit non-IBus IM to try IBus when ibus-daemon is running; unbridged compositors still hit the existing denylist. Fixes #504

  • Jatin K Malik (13 Jul 26)

    feat(asr): FunASR/SenseVoice support for the Remote API engine (#468) * feat(asr): add chat audio remote benchmarking * style(asr): apply black formatting * test(main): include remote API model kwarg * test(asr): add coverage for chat-completions error handling and parsing Add 10 new tests to cover uncovered lines in PR #468: - ConnectionError handler in _try_chat_completions_audio_api (lines 1601-1602) - _parse_chat_completion_transcription edge cases: None, list content, empty/whitespace strings, JSON without text field, plain text, and single-line language metadata (lines 1610, 1613-1621, 1625, 1629-1640) Coverage for recognition_manager.py improved from 85% to 86%. * feat(asr): support FunASR remote responses * refactor(asr): scope remote FunASR support to the #469 path Drop chat-completions/Qwen client, evaluation notes, and the remote ASR benchmark harness. Keep model field, tolerant OpenAI response parsing, SenseVoice label stripping, FunASR docs, and tests.

  • Jatin K Malik (13 Jul 26)

    style(whispercpp): drop long comment on default thread count (#505) Per review on #492, the rationale lives in the PR/history via git blame.

  • only21mil (13 Jul 26)

    fix(whispercpp): don't default to all CPU cores on hybrid processors (#492) The CPU-only auto thread count was `min(cpu_count(), 8)`. On an 8-core hybrid CPU (e.g. Intel Core Ultra, P+E cores) this selects 8 = every core. whisper.cpp synchronises all worker threads at each decode step, so pulling the slow efficiency cores into the pool makes every barrier wait on the slowest core, and leaves nothing for the OS / mic capture. Measured on a Core Ultra 7 355 (8 cores, no SMT), base model, 6s clip: n_threads=6 -> RTF 0.14x n_threads=8 -> RTF 2.13x (~15x slower, i.e. slower than real time) Default to `max(1, min(cpu_count() - 2, 8))`, which leaves headroom and picks 6 on that machine (optimal in a sweep) while staying <= the previous value everywhere else. Users can still override via the `whispercpp_n_threads` config setting. Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: Jatin K Malik <[email protected]>

Vocalinux Website

Website

Vocalinux: Offline Voice Dictation for Linux

Free and open-source voice dictation for Linux. Convert speech to text with whisper.cpp, VOSK, Remote API servers, Silero VAD, X11, and Wayland.

Redirects

Does not redirect

Security Checks

All 65 security checks passed

Server Details

  • IP Address 185.199.110.153
  • Hostname cdn-185-199-110-153.github.com
  • Location Francisco, Indiana, United States of America, NA
  • ISP GitHub Inc.
  • ASN AS54113

Associated Countries

  • US US

Safety Score

Website marked as safe

100%

Blacklist Check

vocalinux.com was found on 0 blacklists

  • AntiSocial Blacklist
  • Artists Against 419
  • Badbitcoin
  • Bambenek Consulting
  • CERT Polska
  • CoinBlockerLists
  • CRDF
  • CryptoScamDB
  • EtherAddressLookup
  • EtherScamDB
  • Fake Website Buster
  • MetaMask EthPhishing
  • NABP Not Recommended Sites
  • OpenPhish
  • PetScams
  • PhishFeed
  • PhishFort
  • Phishing.Database
  • PhishStats
  • PhishTank
  • Phishunt
  • RPiList Not Serious
  • Scam.Directory
  • SecureReload Phishing List
  • Spam404
  • StopGunScams
  • Suspicious Hosting IP
  • ThreatFox
  • ThreatLog
  • TweetFeed
  • URLhaus
  • ViriBack C2 Tracker

Website Preview

Website preview

Vocalinux Reviews

More Accessibility

⚠️ This section is still a work in progress ⚠️
Check back soon, or help us complete it by submiting a pull request on GitHub.
Or submit an entry here

About the Data: Vocalinux

Change History

API

You can access Vocalinux's data programmatically via our API. Simply make a GET request to:

https://api.awesome-privacy.xyz/v1/services/vocalinux

The REST API is free, no-auth and CORS-enabled. To learn more, view the API Docs or read the API Usage Guide.

Share Vocalinux

Help your friends compare Accessibility, and pick privacy-respecting software and services.
Share Vocalinux and Awesome Privacy with your network!

View Accessibility (1)