Hammerspoon
Staggeringly powerful macOS desktop automation with Lua
Features
- Lua scripting
- System API access
- Window management
- App automation
- Hotkeys
- Spoons plugins
Install
$brew install --cask hammerspoonAbout
macOS automation framework with Lua scripting. Window management, hotkeys, app automation. Community 'Spoons' for pre-built functionality.
Who It's For
Hammerspoon is for macOS users who want to automate their entire desktop environment — not just window positions, but app switching, hotkey sequences, Wi-Fi event triggers, clipboard management, and more. If you've outgrown Rectangle's fixed snap positions and want to write logic like "when I connect to my external display, arrange these five apps across three spaces in exactly this layout," Hammerspoon is the right tool. The tradeoff is that everything is code: there's no GUI, and you'll need to be comfortable reading Lua.
How It Works
Hammerspoon bridges Lua scripts to a comprehensive set of macOS system APIs via its hs module namespace. Window management lives in hs.window and hs.layout — you query windows by app name, title, or screen, then move and resize them using frame geometry. Hotkeys are bound via hs.hotkey.bind(). The event system (hs.screen.watcher, hs.wifi.watcher, hs.application.watcher) lets you react to system events rather than just respond to keypresses.
The config file (~/.hammerspoon/init.lua) is loaded at startup and reloaded on demand. Hammerspoon's Spoons system provides a community package layer — pre-built modules for common tasks like window grid management, app launchers, and pomodoro timers — so you can adopt other people's solutions without writing everything yourself.
Configuration
A minimal window management setup in Hammerspoon:
-- ~/.hammerspoon/init.lua
local hyper = {"cmd", "alt", "ctrl"}
-- Snap to left/right halves
hs.hotkey.bind(hyper, "h", function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen():frame()
f.x = screen.x
f.y = screen.y
f.w = screen.w / 2
f.h = screen.h
win:setFrame(f)
end)
-- Reload config
hs.hotkey.bind(hyper, "r", function()
hs.reload()
hs.alert.show("Config reloaded")
end)
-- Auto-reload on save
hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", hs.reload):start()
Most users build up a larger init.lua over time, or split it into modules loaded with require.
Compared to Similar Tools
Compared to Phoenix, Hammerspoon is batteries-included: it ships with APIs for screens, audio, battery, Wi-Fi, USB, drawing, HTTP, and more, while Phoenix exposes a minimal JavaScript API focused specifically on window management. Phoenix is the right choice if you want a lightweight JS-scriptable window manager with no extras; Hammerspoon is right if you want to automate macOS broadly and window management is one piece of that.
Hammerspoon also renders Mjolnir (its predecessor) largely obsolete — Hammerspoon was originally a fork of Mjolnir with a richer built-in API, and the community has since consolidated around it.
Requirements
- macOS 11 Big Sur or later (recent versions)
- Accessibility permission required for window management: System Settings → Privacy & Security → Accessibility
- No SIP changes required
- Free and open-source (MIT license)
Getting Started
brew install --cask hammerspoon
# Launch Hammerspoon, grant Accessibility permission when prompted
# Create ~/.hammerspoon/init.lua and start writing Lua
The official getting started guide walks through the core APIs. The Spoons index lists community modules — SpoonInstall lets you install and manage Spoons directly from your init.lua.