Hammerspoon Script

VeraVox Control

A macOS media-key routing layer that gives VeraVox deterministic control when its playback engine is active, while preserving targeted control of other media players when it is not.

Year
2026
Role
Design / development
Platform
macOS
Status
Working prototype
  • Lua
  • Hammerspoon
  • macOS Accessibility
  • AppleScript
  • Git

Overview

VeraVox Control is a macOS media-key routing layer built in Hammerspoon. It was created to make hardware media keys and keyboard shortcuts behave predictably around VeraVox, particularly when VeraVox has exclusive control of the audio device.

The core problem is not sending play or pause. It is deciding which player should receive a command when several media applications are open, when VeraVox is running but not yet initialized, or when its playback engine has been deliberately stopped.

The problem

macOS media-key behavior becomes ambiguous when multiple players are open at the same time.

A Play command can wake Apple Music unexpectedly. An idle player can capture input even though it is not the intended target. VeraVox can also be open without its engine being ready, which makes a simple foreground-window rule insufficient.

The control layer therefore treats routing as explicit state rather than allowing the operating system to decide implicitly.

Routing model

The current implementation follows a deterministic priority model:

  1. No media player open — Play launches VeraVox.
  2. VeraVox open but not initialized — the first Play command primes VeraVox rather than falling through to Apple Music.
  3. VeraVox engine active — VeraVox owns the media keys regardless of which other player or window is visible.
  4. VeraVox engine stopped — media commands are routed to the focused media player.
  5. No eligible player focused — routing defaults back to VeraVox.
  6. Idle secondary players — an open but inactive player such as Vidi does not take ownership simply because it exists.

This makes exclusive DAC ownership the strongest signal, with window focus used only after VeraVox has released that priority.

Controls

The working control set includes:

  • Space — Play / Pause when the VeraVox window is focused.
  • Shift + Space — Stop and release VeraVox playback priority.
  • Right Arrow — Next track.
  • Left Arrow — Previous track.
  • Media Play — Play / Pause.
  • Media Next / Fast Forward — Next track.
  • Media Previous / Rewind — Previous track.

Targeted player control

Different players are controlled through the most reliable mechanism available for each application.

Apple Music and Spotify use direct AppleScript commands. Vidi and other players use targeted keyboard delivery rather than allowing a generic system media-key event to escape to whichever application macOS chooses.

That separation prevents one player’s fallback behavior from becoming another player’s unintended command.

Accessibility and window state

VeraVox interaction relies on macOS Accessibility information where direct application APIs are not available.

The implementation accounts for the VeraVox window being visible, minimized, resized, closed with the red window control, or reopened. Accessibility elements are re-resolved when necessary rather than assuming a previously discovered UI element remains valid indefinitely.

This became important after window resizing invalidated earlier accessibility anchors.

Failure cases addressed

Several behaviors were corrected through iterative testing:

  • Apple Music launching after the first Play command when VeraVox was open but uninitialized.
  • Spacebar control only working after the VeraVox engine had already started.
  • Closed VeraVox windows causing unintended menu highlighting.
  • Queue reload behavior restarting playback from the first track instead of preserving pause / resume state.
  • Accessibility targets becoming stale after window resizing.
  • An idle Vidi window causing Play to fall through to Apple Music.
  • Multiple open players creating ambiguous ownership when none was focused.

Each fix tightened the routing rules rather than adding another generic fallback.

Current state

The Hammerspoon implementation is working and serves as the current production version of the control layer.

The script is maintained alongside the rest of the macOS automation configuration, with known-good checkpoints used to preserve stable routing behavior while changes are tested.