Hammerspoon on a Mac: scripting the layout you keep rebuilding
Most people arrive at Hammerspoon after doing the same thing for the third week running: dragging a terminal to the left half of the screen, a browser to the right, a chat window onto the second display, and then watching all of it come apart the next time a cable is unplugged. The Mac has a green button and a set of tiling options, and they are not the problem. The problem is that the arrangement lives in someone's hands rather than in a file.
Hammerspoon is one answer to that. It is free, it is not a window manager, and it will not do anything at all until something is written. What follows is what it is, what the first hour looks like, what the current release requires, and where the approach stops working.
What Hammerspoon is, stated plainly
Hammerspoon's own site describes it as a tool for powerful automation of macOS, and then says something more useful: at its core it is just a bridge between the operating system and a Lua scripting engine. The power comes from a set of extensions that expose specific pieces of system functionality.
That is worth reading twice, because it sets expectations correctly. There is no preferences pane full of layout presets. There is a Lua runtime with access to macOS, and a menu bar icon.
The documented surface is wide. Hammerspoon's extensions reach applications, windows, mouse pointers, filesystem objects, audio devices, batteries, screens, low-level keyboard and mouse events, clipboards, location services and wifi. Window management is simply the extension people find first.
The current release is 1.1.1, published on 26 February 2026, and its release note states a minimum macOS version of 13.0. The project is MIT licensed and distributed on GitHub, where it has around 16,000 stars. Homebrew carries it as a cask under the token hammerspoon, currently at the same 1.1.1.
The install instruction from the site is two steps: download the latest release, then drag the application to /Applications/. On first run it asks for Accessibility access, which macOS requires before any application can move another application's windows. Nothing works until that is granted.
Then comes the sentence that catches people out. Out of the box, Hammerspoon does nothing. A Lua script has to be created at ~/.hammerspoon/init.lua, and the menu bar icon has an Open Config item that puts it in front of you.
The first hour, honestly
The official Getting Started guide opens with a hotkey binding, and it is a fair picture of the whole experience:
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
hs.alert.show("Hello World!")
end)
Save, click the menu bar icon, choose Reload Config, and the chord shows an alert. Moving a window is the same shape: fetch the focused window, read its frame, change the numbers, set the frame back.
The guide then spends a section on something that no comparison article mentions and that will cost an evening if it is missed. Lua garbage collects, and init.lua is treated as a single scope that finishes when the last line runs. Any object created there and not captured in a variable becomes eligible for collection. A path watcher started with hs.pathwatcher.new(...):start() and never assigned will keep working for minutes or hours and then quietly stop. Assigning it to a global variable is what keeps it alive. The documentation states this outright, and it is the single most common reason a configuration that worked yesterday has half stopped today.
The realistic cost of the first hour is not learning Lua. It is learning that Hammerspoon is a program you now maintain. Config reloading, window filters, multi-window layouts and smart reloading through Spoons all have sections in the guide, which is another way of saying they are all decisions waiting to be made.
Window management is the entry point and also the ceiling
The guide works up to the common case: a hotkey that puts the focused window on the left or right half of the screen. That is genuinely useful, and it is also the part where the built-in system has caught up.
Apple documents window tiling in macOS 27 Golden Gate through the green button. Holding the pointer over it shows Move & Resize options that fill the left, right, top or bottom half of the screen with the menu bar and Dock still visible, and a Fill & Arrange section for filling the screen. Dragging a window close to another aligns it without overlapping, and Fn-Control-F fills the screen from the keyboard. Notably, Apple's page documents the halves through the green button menu and does not document keyboard shortcuts for them.
So the honest case for scripting is not halves. It is everything the green button has no concept of: thirds and sixths, a named layout applied to five specific applications at once, rules that depend on which display is attached, and a hotkey that does all of it in one keystroke rather than four pointer gestures.
| Built-in macOS tiling | Hammerspoon | yabai with skhd | |
|---|---|---|---|
| Price | Included with macOS | Free, MIT licensed | Free, MIT licensed |
| How layouts are defined | Pointer, per window | Lua in ~/.hammerspoon/init.lua |
Config file and command line |
| Automatic tiling of new windows | No | Only if written | Yes, binary space partitioning |
| Accessibility permission | Not applicable | Required | Required |
| System Integrity Protection | Untouched | Untouched | Partially disabled for some features |
| Reaches beyond windows | No | Yes, many system APIs | Windows, spaces and displays |
| Keyboard shortcuts | Fn-Control-F to fill | Any, defined in Lua | Defined in skhd |
The yabai column is there because it is the other name that comes up, and the difference in kind matters more than the feature counts. yabai's own documentation states that System Integrity Protection can be partially disabled so it can inject a scripting addition into Dock.app, and its wiki lists what depends on that: moving, swapping, creating and destroying spaces, window shadows, transparency, animations, scratchpad windows, window layers, sticky windows and picture-in-picture. Hammerspoon asks for Accessibility and stops there. Which of those two trades is acceptable is a personal answer, not a technical one.
The half of Hammerspoon that is not about windows
The table of contents of the official guide is the best argument for the tool, and window sizing is only a few lines of it. The rest covers reacting to application events, reacting to wifi events, reacting to USB events, interacting with application menus, creating a menu bar item, drawing on the screen, running AppleScript, defeating paste blocking, sending iMessage or SMS messages, and driving Hammerspoon itself from URLs.
That list is where the interesting configurations live. A rule that mutes the audio output when the work wifi network appears. A menu bar item that shows one number that matters. A hotkey that opens a specific application menu item that has no shortcut of its own. None of these are window management, and all of them are the same three lines of Lua in a different extension.
Spoons are the part that shortens the path. They are pre-made plugins with an official listing and an official repository, installed by double clicking a .spoon file, then loaded and bound explicitly:
hs.loadSpoon("AClock")
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "C", function()
spoon.AClock:toggleShow()
end)
Installing a Spoon does not make it run. The documentation says so directly, and it is the second most common cause of a configuration that appears to do nothing.
The Console deserves a mention of its own, because it is where most of the debugging happens and it behaves in a way that surprises people. Each line typed into it is created, executed and finished as a distinct Lua scope, so a local variable defined there is already out of reach by the time the next line is typed. The guide states this explicitly. The practical rule is to prototype in the Console with globals and move the working version into init.lua afterwards.
Support is worth checking before committing an evening. The project runs a Discord server for questions and takes bug reports and suggestions through the GitHub issue tracker. The API documentation is published alongside the guide, and reading the extension list once is the fastest way to find out whether the thing being attempted is a three-line binding or a project.
What the official FAQ warns about
The project's FAQ is short and unusually candid, and reading it before installing saves real time.
Accessibility is the recurring theme. Hammerspoon depends on the macOS accessibility stack, and macOS requires the user to grant that manually. If the permission cannot be granted or stops working, the FAQ points at two causes: the database controlling accessibility access can desynchronise from reality, and the Launch Services database can become corrupt, which it suggests fixing with an lsregister rebuild. Where keystrokes stop reaching other applications after a macOS upgrade, the suggested fix is resetting the application's permissions with tccutil reset All org.hammerspoon.Hammerspoon.
Three smaller traps are documented. Microsoft Office applications present different names to the system than the ones in the menu bar, so Microsoft Word is the string to look for rather than Word. Many applications mark their menus as disabled when their windows lose focus, which is why findMenuItem and selectMenuItem often fail when tested from the Hammerspoon Console rather than from a hotkey. And no graphical elements appear over a full-screen application while the Dock icon is enabled, which hs.dockIcon(false) resolves.
One more thing worth knowing: crash reporting is on by default and can be turned off in the preferences. The project also declines donations, and asks instead that the money go to a charity of the reader's choosing.
The layer a window script cannot reach
Every tool in the table above shares one blind spot. They operate on windows and applications, and they have no idea what is inside one.
For most people reading this, that is where the time actually goes. The browser is one application with one window and thirty things in it: a work inbox, a personal inbox, a shared team inbox, two chat tools, a project tracker, a design file, a support queue. A Lua script can put that window on the left half of the display in twenty milliseconds. It cannot tell the work inbox from the personal one, because to the accessibility API they are the same window, and to the filesystem they do not exist at all.
The tool for that layer is a browser that treats each web app as an application rather than a page. Giving every app its own window and its own cookie container means two accounts on the same service stay signed in at the same time, and each workspace is reachable by name instead of hunted for among identical tabs. It sits alongside a window script rather than replacing it, since the two solve different halves of the same complaint, and the list of supported apps is the quickest way to check whether the specific tools in question are covered. Anyone who already lives in a terminal will find the built-in shell removes one more window from the arrangement entirely.
What to change first
Write the smallest init.lua that binds one hotkey to one layout, use it for a week, and add nothing until that first binding is automatic. If the week shows the friction was never the local windows but the forty web apps stacked inside one browser window, the fix belongs at that layer instead, which is what SpaceDeck is built for.
Frequently asked questions
Is Hammerspoon free, and is it still maintained?
It is free and MIT licensed, with the source on GitHub. The current release is 1.1.1, published on 26 February 2026, and its release note sets the minimum macOS version at 13.0. The same version is available through Homebrew as the cask hammerspoon.
Why does nothing happen after installing Hammerspoon?
Because nothing is supposed to. The official site states that out of the box Hammerspoon does nothing, and that a Lua script has to be created at ~/.hammerspoon/init.lua. The other common cause is Accessibility access: macOS requires it to be granted manually before one application can control another's windows.
Does Hammerspoon need System Integrity Protection disabled?
No. It asks for Accessibility access and, in the FAQ's troubleshooting steps, nothing beyond permission resets. That is a real difference from yabai, whose documentation describes partially disabling System Integrity Protection so a scripting addition can be injected into Dock.app for features such as creating and destroying spaces, sticky windows and animations.
Hammerspoon or the tiling built into macOS?
If the need is left half and right half, the built-in green button already covers it in macOS 27, along with top and bottom halves, edge alignment and Fn-Control-F to fill the screen. Scripting earns its keep for layouts the green button has no concept of, such as thirds, named multi-application arrangements and rules that change with the attached display.
My configuration worked yesterday and half of it stopped. Why?
The most likely cause is documented in the Getting Started guide. Lua garbage collects, and init.lua is a single scope that ends at the last line, so any object created there without being captured in a variable becomes eligible for collection and can stop working minutes or hours later. Assigning watchers and timers to variables that stay in scope prevents it.
Can Hammerspoon tell two Gmail accounts apart?
No. Hammerspoon works with applications and windows, and every tab in a browser belongs to the same application and the same window. Keeping several accounts on one service usable at the same time is a browser-level problem, solved by per-app windows with separate cookie containers rather than by a window script.