An ode to SizeUp

An ode to SizeUp

A fond farewell to SizeUp and a 5-minute switch to Hammerspoon

Sizing up #

SizeUp had a great run #

SizeUp was a paid Mac window manager that did one thing perfectly: keyboard shortcuts to snap windows left, right, full-screen, and quarters. I happily paid for it years ago after a friend introduced me to it.

It changed how I used my Mac daily. No more dragging window edges around. Just muscle memory.

The problem: SizeUp requires Rosetta on Apple Silicon. I don’t want to install Rosetta for one app on an M5 Mac. The developer (Irradiated Software) has gone quiet — the app still technically works via Rosetta, but it’s not getting updates.1

Most people still wrestle with window management #

It’s surprising how many technical people still drag-resize windows by hand.

If you’re on Mac and don’t have a window manager, fix that today. It takes 5 minutes.

The whole trick: one position #

The magic isn’t the shortcuts — it’s that you never move your hands. The three modifier keys you hold live together at the bottom-left corner of every Mac keyboard.

The Control, Option, and Command keys sit together at the bottom-left of a Mac keyboard — hold all three as a single position.

From there, you change exactly one key for each mode — your hand never leaves the corner:

No new shortcuts to memorize. Hold the corner, change a key, point a direction.

Hammerspoon: the 5-minute replacement #

Hammerspoon is a free, open-source, scriptable Mac automation tool.2 It uses Lua for configuration — a simple scripting language you don’t need to learn deeply. Window management is just one of many things Hammerspoon can do, but it’s the killer use case.

Install:

brew install hammerspoon

Configure — create ~/.hammerspoon/init.lua:

local hyper = {"ctrl", "alt", "cmd"}          -- halves + maximize
local quad  = {"ctrl", "alt", "cmd", "shift"} -- quarters (add Shift)
local toMon = {"ctrl", "alt"}                  -- move to another display

- On NON-primary displays, shave a gap off the bottom so a snapped window
- never sits flush against the edge and drags the Dock over. [see note below]
local GAP = 100

- Usable area of a screen. screen:frame() already excludes the menu bar and
- Dock; we shrink it further only on secondary monitors.
local function usable(screen)
  local m = screen:frame()
  if screen ~= hs.screen.primaryScreen() then m.h = m.h - GAP end
  return m
end

- Snap the focused window to a fraction of the usable area.
local function snap(x, y, w, h)
  return function()
    local win = hs.window.focusedWindow()
    if not win then return end
    local m = usable(win:screen())
    win:setFrame({ x = m.x + m.w * x, y = m.y + m.h * y, w = m.w * w, h = m.h * h })
  end
end

- Halves + maximize
hs.hotkey.bind(hyper, "Left",  snap(0,   0,   0.5, 1))
hs.hotkey.bind(hyper, "Right", snap(0.5, 0,   0.5, 1))
hs.hotkey.bind(hyper, "Up",    snap(0,   0,   1,   0.5))
hs.hotkey.bind(hyper, "Down",  snap(0,   0.5, 1,   0.5))
hs.hotkey.bind(hyper, "M",     snap(0,   0,   1,   1))

- Quarters (clockwise: Up = top-left, Right = top-right, Down = bottom-right, Left = bottom-left)
hs.hotkey.bind(quad, "Up",    snap(0,   0,   0.5, 0.5))
hs.hotkey.bind(quad, "Right", snap(0.5, 0,   0.5, 0.5))
hs.hotkey.bind(quad, "Down",  snap(0.5, 0.5, 0.5, 0.5))
hs.hotkey.bind(quad, "Left",  snap(0,   0.5, 0.5, 0.5))

- Move the window to the next / previous display
hs.hotkey.bind(toMon, "Right", function()
  local win = hs.window.focusedWindow()
  if win then win:moveToScreen(win:screen():next()) end
end)
hs.hotkey.bind(toMon, "Left", function()
  local win = hs.window.focusedWindow()
  if win then win:moveToScreen(win:screen():previous()) end
end)

That’s it. Reload the config (Hammerspoon menu bar → Reload Config) and you’re done.

The bottom gap on secondary monitors: notice GAP and usable() above. macOS shows the Dock on whichever display your pointer reaches the bottom edge of, and a window snapped flush to the bottom of a second monitor makes it far too easy to summon the Dock onto that screen by accident. Shaving a gap off the bottom of non-primary displays keeps every window just shy of the edge, so the Dock stays put. There’s no clean way to pin the Dock to one display — the gap is the pragmatic fix.3

Other options worth knowing about #

Other platforms #

Appendix #

VSCode/Cursor tip: Set Ctrl+Cmd+Arrow to move editor tabs between split groups. Pairs nicely with window snapping for a fully keyboard-driven layout.


Footnotes #

  1. SizeUp by Irradiated Software. Still available at http://www.irradiatedsoftware.com/sizeup/ for $12.99, but requires Rosetta 2 on Apple Silicon and hasn’t been updated for native ARM support.
  2. Hammerspoon — free, open-source macOS automation tool. Lua-based config. https://www.hammerspoon.org/
  3. The root cause is the "Displays have separate Spaces" behavior (System Settings → Desktop & Dock). With it on — which it is by default, and which you want if you ever full-screen apps per monitor — macOS treats each display as its own Space and parks the Dock on whichever screen your pointer last hit the bottom of. There’s no setting to lock the Dock to one display while keeping per-monitor full-screen Spaces, so a small bottom gap on secondary screens is the practical workaround.
  4. Rectangle — free, open-source window manager for macOS. https://rectangleapp.com/