Hammerspoon

Staggeringly powerful macOS desktop automation with Lua

Category
Scripting
WM Type
License
free
Open Source
Yes
Keybindings
Yes
Scripting
Yes
Last Update
2026-02-26

Features

  • Lua scripting
  • System API access
  • Window management
  • App automation
  • Hotkeys
  • Spoons plugins

Install

$brew install --cask hammerspoon

About

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.

Discussion