22 Jan 2026

Overview
Hammerspoon is a lightweight macOS automation tool built on Lua. It exposes low-level macOS APIs for windows, screens, mouse, keyboard, and system events, making it ideal for deterministic, keyboard-driven workflows.
Why it’s useful:
- Full control over the mouse cursor (e.g. move to focused window)
- Precise window and screen awareness (multi-monitor safe)
- Instant execution (no UI lag)
- Declarative, reproducible configuration (dotfile-friendly)
Typical use cases:
- Warp mouse to the focused window after app switching
- Keyboard-driven window positioning across monitors
- Custom app switchers that fix Cmd+Tab shortcomings
- Context-aware shortcuts (screen, app, window state)
Strengths:
- Free and open-source
- Extremely reliable
- Composable (small functions, clear intent)
- Excellent fit as a “logic layer” behind hardware keyboards and launchers
Trade-offs:
- Requires basic Lua
- No GUI for building automations
- You own the config (feature, not a bug)
Ideal for me because, best paired with:
- Alfred (intent + discovery)
- Kinesis Advantage360 (hardware triggers / layers)
- Keyboard-only workflows on multi-monitor setups
Mental model:
Hardware key → Launcher (optional) → Hammerspoon → macOS APIs
In practice, Hammerspoon is the missing glue that makes macOS behave predictably when you refuse to touch the mouse.
How do I use it?
Out of the box, Hammerspoon does nothing.
You will need to create a Lua script in ~/.hammerspoon/init.lua using our APIs and standard Lua APIs.
If you are new to Hammerspoon, read the:
- Getting Started Guide: https://www.hammerspoon.org/go
- full API documentation: https://www.hammerspoon.org/docs
Why using it (with Alfred and Amethyst already in use)
Short answer: logic, state, and system hooks.
Amethyst and Alfred are great at actions. Hammerspoon excels at decisions.
Here’s what Hammerspoon can do that they fundamentally can’t (or only very awkwardly).
- Conditional, Context-Aware Automation
Example
Same hotkey, different behavior:
• If Teams focused → mute mic
• If browser focused → mute tab
• Else → mute system audio
Amethyst: ❌
Alfred: ⚠️ (hacky, brittle)
Hammerspoon: ✅ native
This is where Lua + app/window state matters.
- Reacting to System Events (not user-triggered)
Things that happen to your Mac:
• Display connected / disconnected
• Wi-Fi network changed
• Sleep / wake
• Audio device appeared
• App crashed or relaunched
Amethyst: ❌
Alfred: ❌ (mostly pull-based)
Hammerspoon: ✅ push-based
Example:
• HDMI monitor appears → move demo windows + switch audio + warp mouse
- True Mouse Control (not just triggers)
You already hit this.
• Move cursor to focused window
• Jump cursor between monitors
• Cursor follows Cmd+Tab focus
• Eliminate mouse entirely
Alfred: ❌
Amethyst: ❌
Hammerspoon: ✅ (one of its killer features)
- Stateful Automations (memory across actions)
Hammerspoon can remember things:
• Last audio device
• Last window layout
• Current “mode” (Demo / Meeting / Focus)
• Toggle cycles beyond on/off
Example:
• Press once → setup demo
• Press again → restore previous state
Alfred workflows reset every run.
Hammerspoon keeps state alive.
- App-Lifecycle Enforcement
Rules like:
• “Teams must never open on monitor 1”
• “Safari popups always float + move”
• “If Zoom launches, kill Music”
This happens automatically, not via shortcuts.
Amethyst: limited, layout-only
Alfred: manual
Hammerspoon: declarative + automatic
- Deep Audio & Device Logic
You’re in audio hell already 😄
Hammerspoon can:
• Detect audio devices appearing/disappearing
• Enforce preferred output
• Auto-fix macOS “helpful” switching
• Per-context audio rules
Alfred: ❌
Amethyst: ❌
Hammerspoon: ✅
- Accessibility-Level UI Automation
When apps don’t expose APIs:
• Click buttons
• Navigate menus
• Read UI state
• Build robust macros
More reliable than AppleScript, more programmable than Alfred.
- Glue Between Tools (this is the real win)
Hammerspoon doesn’t replace:
• Amethyst → window tiling engine
• Alfred → launcher + UI
It orchestrates them.
Example:
• Hammerspoon detects context
• Calls Amethyst layout change
• Triggers Alfred workflow
• Fixes mouse + audio after
Alfred alone cannot observe system state.
Amethyst cannot decide.
Mental Model (important)
• Amethyst: “Where should windows go?”
• Alfred: “Run this now”
• Hammerspoon: “Given what’s happening, what should my system do?”
If you’re happy with:
• Static shortcuts
• Manual triggers
• Stateless workflows
→ You don’t need Hammerspoon.
If you want:
• Deterministic behavior
• Zero-click corrections
• Keyboard-only, multi-monitor sanity
→ Hammerspoon becomes the control plane.
High-ROI Uses for you specifically
Given your setup, the biggest wins would be:
1. Cursor-follows-focus (you already felt it)
2. Audio device enforcement
3. Demo / Meeting mode toggles
4. App launch + window correction rules
5. “Fix everything” panic hotkey
If you want, next I can:
• Sketch a minimal Hammerspoon config that coexists with Amethyst + Alfred
• Show exact examples that are impossible in Alfred
• Design a single hotkey that resets your entire demo setup
Use cases & code
Position mouse (using Kinesis + Alfred)
Goal: Move the mouse cursor to the center of the currently focused window after switching apps (e.g. via Cmd+Tab), using:
- Kinesis Advantage360 (hardware trigger)
- Alfred (launcher / glue)
- Hammerspoon (window + mouse logic)
Step 1 — Choose a dedicated shortcut
Pick a shortcut that:
- Is easy to hit from the Advantage360 (thumb cluster is ideal)
- Does not conflict with system shortcuts
Recommended:
- Hyper + M
(Hyper = Ctrl + Alt + Cmd + Shift)
You can change this later, but pick one and stick to it.
My choice = CAPS key?
Step 2 — Program the Kinesis shortcut
In SmartSet:
- Assign a key (or thumb key) to emit:
Ctrl + Alt + Cmd + Shift + M
- Keep it as a simple key combo (no delays, no sequences)
Result:
Pressing that key sends a single, clean shortcut to macOS.
Step 3 — Create an Alfred workflow
1. Open Alfred Preferences
2. Go to Workflows → New Workflow
3. Name it something like: “Warp Mouse to Window”
Add a trigger:
- Hotkey Trigger
- Shortcut: Ctrl + Alt + Cmd + Shift + M
- Set it to “Pass through to workflow”
Add an action:
- Run Script
- Language: /bin/bash
- Script:
hs -c 'warpToFocusedWindow()'
Connect:
Hotkey → Run Script
Step 4 — Add the Hammerspoon function
Open ~/.hammerspoon/init.lua and add:
function warpToFocusedWindow()
local win = hs.window.focusedWindow()
if not win then return end
local f = win:frame()
hs.mouse.absolutePosition({
x = f.x + f.w / 2,
y = f.y + f.h / 2
})
end
Reload Hammerspoon config.