Voice control for Omarchy, powered by the Gemini Live API.
Press a key, talk normally, and your desktop does what you asked — while you're still talking. Beckon streams audio both directions over a single connection, so there is no record-then-wait-then-respond cycle: it answers mid-sentence and runs actions as it hears them.
Independent project. Beckon is built for Omarchy users but is not affiliated with, endorsed by, or part of Omarchy, its maintainers, Hyprland, or Google.
Beckon exposes your desktop to Gemini as tools it can call, and chains them itself:
- Windows — focus, move, resize, swap, tile, float, fullscreen, close
- Workspaces — switch, send windows between them, move a workspace to another monitor
- Typing — type text, press keys, send shortcuts into the focused window
- Mouse — move the pointer, click
- Vision — screenshot the screen and answer questions about what's on it; a soft orange edge glow shows while it's looking
- Reading — pulls the full text of a page, email or document straight from the window, including everything scrolled off screen, so it answers without scrolling; it can use the text, the picture, or both (setup)
- Keyboard shortcuts — reads your current Omarchy keybindings live and can trigger any of them by name (terminal, browser, screenshot, emoji picker...); rebind a key and it follows
- Clipboard — read and write
- System — volume, brightness, launch apps, open URLs
- Your own tools — add any shell command as a voice-callable tool from the panel
- Memory — remembers preferences ("email means Gmail") and short notes about what you're working on, so it stops asking and gets more useful over time
"Put Chrome and my editor side by side", "what does this error say?", "open the weather and read it to me" all work as single requests.
Made for Omarchy — built and tested on Omarchy 4.0.3.
Omarchy runs on Hyprland, and Beckon drives the desktop through Hyprland's dispatcher API, so other Hyprland-based setups can run it too with a little adjustment:
- Hyprland has to be underneath. It will not work on GNOME, KDE, X11, or other Wayland compositors.
- On a non-Omarchy Hyprland setup, the guided tour's theme switching (which
uses the
omarchyCLI) won't apply, and app launching expectsuwsm-app(swap it inbeckon/tools.pyif you don't use uwsm). - Hyprland builds without the Lua dispatcher API need the calls in
beckon/tools.pyadjusted to the classichyprctl dispatchsyntax.
| Needed for | Arch package | Notes |
|---|---|---|
| Runtime | python 3.11+ |
|
| Live API connection | python-websockets |
official repo |
| Mic and speaker streaming | python-sounddevice |
official repo (Omarchy also ships it) |
| Gemini SDK | python-google-genai |
AUR |
| Typing and key presses | wtype |
official repo |
| Screen vision | grim |
official repo |
| Clipboard | wl-clipboard |
official repo |
| Mouse clicks (optional) | ydotool |
needs its daemon — see Mouse clicks |
| Reading page text (optional) | at-spi2-core, python-gobject |
see Reading page text |
| Notifications | libnotify |
notify-send; already on Omarchy |
| Narration playback | pipewire-pulse |
paplay; already on Omarchy |
| Music control in the tour | systemd |
busctl for MPRIS; already there |
| Theme switching in the tour | omarchy CLI |
Omarchy only |
| Model access | Gemini API key | automatic setup needs Google Cloud CLI; manual fallback is AI Studio |
sudo pacman -S --needed python-websockets python-sounddevice wtype grim wl-clipboard ydotool libnotify
yay -S --needed python-google-genaiAs a package (recommended) — pacman then owns the files, pacman -R beckon-git removes them cleanly, and rebuilding picks up the latest commit:
yay -S python-google-genai # the one dependency not in the official repos
git clone https://github.com/Steven-Tibbs/beckon.git
cd beckon/packaging && makepkg -simakepkg -si builds the package and invokes pacman -U itself. If you prefer
to inspect the generated package before installing it, run makepkg -s and
then sudo pacman -U ./beckon-git-*.pkg.tar.zst.
Or from source, if you'd rather not package it:
git clone https://github.com/Steven-Tibbs/beckon.git
cd beckon && ./install.shEither way, two more commands:
beckon ui # paste your Gemini API key, then hit Test connection
beckon setup # bind a key — F8 by default, `beckon setup F7` for anotherGet a key free at aistudio.google.com/apikey — that is the fastest way in, and nothing else is required.
beckon setup-api-key creates the key for you instead of pasting one. It signs
in to Google Cloud if needed, enables the Gemini Developer API, creates a
standard key restricted to generativelanguage.googleapis.com, saves it at
~/.config/beckon/api_key with 0600 permissions, and verifies it:
beckon setup-api-key --project PROJECT_ID # --rotate replaces an old keyIt needs the Google Cloud CLI (yay -S google-cloud-cli) and a project that
already exists — pass it above, or set it with gcloud config set project.
Which pricing tier you land on depends on whether that project has an active
billing account, not on how the key was made, so a project you already pay for
will bill this usage too.
Do not restrict the key only to Vertex AI: it needs the Gemini Developer API.
Keys bound to a service account can also fall under organization policies, which
makes changing their restrictions awkward. The Test connection button in
beckon ui checks access without ever displaying the key.
beckon setup appends the binding to ~/.config/hypr/bindings.lua, backs the
file up first, and refuses to take a key that is already spoken for. To do it by
hand instead:
o.bind("F8", "Beckon", "beckon")Press the key once to start listening, again to stop.
Both install methods add Beckon to your app grid — clicking it opens the
control panel. The package puts the entry in /usr/share/applications and the
icon in /usr/share/icons/hicolor/scalable/apps; install.sh puts both under
~/.local/share.
If it doesn't show up, your launcher is holding a stale cache:
update-desktop-database ~/.local/share/applications
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolorSome launchers only rescan on login. If you installed Beckon before the icon
existed, reinstall (makepkg -si or ./install.sh) and it appears.
While Beckon is speaking it closes the mic, so its own voice coming out of your
speakers can't register as you interrupting it. That means you can't talk over
it by voice; if you use headphones and want that back, start it with
BECKON_BARGE_IN=1.
If you use Claude Code, Gemini CLI, or another coding agent, point it at
AGENTS.md — it has the exact steps, the checks that prove each
one worked, and the pitfalls. A prompt that works:
Install Beckon from https://github.com/Steven-Tibbs/beckon on this Omarchy machine by following its AGENTS.md. Verify each step, then open
beckon ui.
beckon |
start a live session (or press your bound key) |
beckon ui |
control panel: API key, model, voice, custom tools, history |
beckon setup |
bind a key — beckon setup F7 to choose another |
| "give me a guided tour" | the demo — it narrates itself |
The tour's last step opens a local dev server. Point it at yours by putting
"dev_url": "http://localhost:3000" (and optionally "dev_line" for what it
says over it) in ~/.config/beckon/settings.json.
Beckon runs on gemini-3.8-live by default. The panel lists every model the
Live API can hold a session with; pick one and it applies to the next session,
however you start it.
| model | what it's for |
|---|---|
gemini-3.8-live |
default — fastest, best all-round |
gemini-3.8-live-extended-thinking |
reasons harder on multi-step tasks; speaks a filler first and takes longer to finish |
gemini-3.1-flash-live-preview |
previous default |
Extended-thinking models need a thinking level; Beckon sends LOW, which you
can change with thinking_level in ~/.config/beckon/settings.json.
Audio goes to Google's Live API; that is what makes the low latency possible.
When you ask it to look at or read something, that goes to Google too: a
screenshot of the focused monitor for look_at_screen, and the window's text
for read_page_text — which is the whole window, including parts scrolled off
screen. Nothing is sent anywhere else, and nothing is sent unless you ask for
something that needs it. Your key lives in ~/.config/beckon/api_key
with 0600 permissions and is never shown in the UI, not even partially.
Conversation history is local (~/.local/share/beckon/history.jsonl) and has
a clear button in the panel. It, your memory, your settings and your custom
tools are all written 0600 — readable only by you, even on a shared machine.
Anything Beckon reads off your screen — a page, an email, the clipboard — is
attacker-controlled text, and a page can hide instructions where you cannot see
them (screen-reader-only CSS, an aria-label, anything below the fold). All of
it reaches the model wrapped in an untrusted-content marker, and the prompt is
explicit that such text is reported, never acted on. Content is also never
written to memory: only what you say out loud is, and you get a notification
when it happens.
That reduces the risk. It does not eliminate it — no prompt rule does. Treat "read me this page" on a site you don't trust the way you'd treat running a script from it.
Beckon keeps a small memory at ~/.config/beckon/memory.json: preferences
(email → https://mail.google.com) and notes about what you're working on.
It reads them at the start of every session and writes to them when you say
things like "remember that I use Gmail" or "I'm working on the checkout
page." For anything generic — email, music, notes, your editor — it uses the
saved preference, and if there isn't one it asks once and remembers the answer.
It is hard-capped at 40 preferences and 30 notes of 200 characters each, so the file stays a few kilobytes forever; oldest entries drop off. Everything in it is visible and deletable in the panel. It never stores secrets or email addresses.
Asked "read me this email", Beckon can take the whole thing from the window at once — including the part below the fold — instead of screenshotting and scrolling. It reads the accessibility tree the desktop already publishes, which is faster, exact, and costs no vision call. Two settings turn it on, and both are needed:
gsettings set org.gnome.desktop.interface toolkit-accessibility true
gsettings set org.gnome.desktop.a11y.applications screen-reader-enabled true
echo '--force-renderer-accessibility' >> ~/.config/chrome-flags.confRestart Chrome afterwards — Chromium reads the accessibility state only at
startup. GTK apps work without the Chrome flag. Needs at-spi2-core and
python-gobject, both in the official repos.
Without this, Beckon still sees the screen; it just sees only what's visible. Terminals, images and video never publish text, so it falls back to looking.
Hyprland's own synthetic clicks are accepted but never reach applications,
so click goes through ydotool. Its daemon needs /dev/uinput, which is
root-only by default. The simplest setup is a system service whose socket is
owned by you:
sudo tee /etc/systemd/system/ydotoold.service >/dev/null <<UNIT
[Unit]
Description=ydotool daemon
[Service]
ExecStart=/usr/bin/ydotoold --socket-path=/tmp/.ydotool_socket --socket-own=$(id -u):$(id -g) --socket-perm=0600
Restart=on-failure
[Install]
WantedBy=multi-user.target
UNIT
sudo systemctl enable --now ydotooldEverything else works without it; only click needs it.
From the panel — Your own tools takes a name, a description (this is
what the assistant reads to decide when to use it), a shell command, and
optional arguments. {arg} placeholders are filled with shell-quoted values.
They are stored in ~/.config/beckon/custom_tools.json and picked up when the
next session starts.
In Python — add a function to beckon/tools.py and list it in TOOLS at
the bottom. The docstring becomes the description, the signature becomes the
schema; nothing else to register.
def lock_screen():
"""Lock the screen immediately."""
return _hypr('hl.dsp.exec_cmd("omarchy-system-lock")')Pull requests are welcome. Fork, branch, open a PR — nothing lands on master
without review by the maintainer, and direct pushes are limited to the owner.
MIT