Shaka

Not a tiling manager. Just a nudge.

Shaka is a 🤙 in your menu bar and a handful of keys under your fingers. Windows move, grow, snap and settle on a spring — nothing to lay out, nothing to learn, and no grid to live inside unless you ask for one.

$curl -fsSL https://raw.githubusercontent.com/wes/shaka/main/install.sh | bash

Free · MIT · macOS 13+ · Xcode Command Line Tools

FinderFileEditViewGoWindowHelp🤙100%Thu 9:41
Safari
‹ ›
shaka — swift build

~/Sites/shaka swift build -c release

[9/9] Compiling Shaka WindowManager.swift

Build complete! (12.84s)

🤙 Shaka Window Manager

─────────────────────────

ctrl + arrows          focus

ctrl + opt + arrows   move

ctrl + cmd + arrows   snap

~/Sites/shaka

Notes
Board meeting
Groceries
Shaka — todo
Rincon trip
Music
▶︎
Click the screen, then use the arrow keys — or the shortcuts below
Focus
+ arrows
Move
⌃⌥ + arrows
Resize
⌃⇧ + arrows
Snap
⌃⌘ + arrows
waiting for a key

That desktop is drawn, not screenshotted, and the windows are not on CSS transitions. Every move, resize and snap runs Shaka's own geometry, integrated by Shaka's own spring at the same fixed 1/120 step the app uses — so the overshoot you feel here is the overshoot you get at animation_damping = 28.

It nudges. It does not tile.

No layout engine deciding where your windows belong, no master pane, no gaps setting to agonise over. You push a window 80 points at a time and it stays exactly where you left it.

It is a menu bar and nothing else.

No dock icon, no preferences window, no onboarding, no update nag. The 🤙 is the whole interface, and the config is a text file you already know how to edit.

It is about 1,200 lines of Swift.

Cocoa, the Accessibility API and a spring, with no third-party packages anywhere in Package.swift. You build it yourself, which means you can read all of it first.

Motion

A window with weight.

Most window managers teleport. Shaka runs a damped spring on all four numbers of the frame at once — origin and size, on a timer at 8ms — so a window leans into the move, arrives, and settles. It is the difference between a window being repositioned and a window going somewhere.

Press a direction twice quickly and it does not stutter: the second press retargets the spring in flight, keeping the velocity it already had, so a held key glides instead of stepping.

Click anywhere on the strip to send the window there. Those two lines go in config.toml exactly as they read.

vel += (k·(target − cur) − c·vel)·dt
cur += vel·dt

Eight lines of physics, run over x, y, width and height. When the remaining distance and the remaining speed both fall under half a point, the window is set exactly on target and the timer stops.

Nothing runs when nothing moves

The animation timer is created for a move and cancelled the moment the window settles. Between keystrokes Shaka is an event tap and a menu, and that is all.

Reduced motion is respected here

This page's demo checks prefers-reduced-motion and jumps straight to the target instead of springing.

Snapping

Press it again for a third.

puts the window on the left half. Press it again and it is a third; again and it is two thirds; again and you are back to half. Four common layouts live on one key, and changing direction starts the cycle over.

First press½
Second press
Third press
½

A half is not half the screen.

It is fraction × (width − padding) − padding — the padding around the outside is spoken for first, and the gutter down the middle comes out of the two halves. Snap one window left and another right and the gap between them is the same 10 points as the gap around them, which is the only version of a half that looks right.

Edges are sticky

A nudge that lands within 20 points of an edge gives up the difference and sits at the padding instead — so windows line up without you aiming.

Resizing works from the centre

Grow a window and it spreads both ways rather than walking off to the right. It stops shrinking at 200 points, whichever key you are leaning on.

Fill is not fullscreen

fills the visible frame minus the padding. No green button, no Space of its own, no animation you have to sit through.

Focus

Arrow keys, but for windows.

⌘-Tab cycles through applications in the order you last used them, which is a different question from the window on the left. Shaka answers the second one: activates the app that owns the nearest window in that direction and raises it.

It reads the on-screen window list in a single call, so it sees every app's windows rather than only its own, and it is over before your finger is off the key.

01

Straight ahead beats close by

Distance is scored with the perpendicular axis weighted four times as heavily, so a window directly left wins over a nearer one off up a corner.

02

Ten points of slack

A neighbour has to be more than ten points away in the direction you pressed, which keeps two windows sharing an edge from swapping back and forth.

03

Only real windows

Layer zero, larger than 50 × 50. Menu extras, tooltips, the desktop and Spotlight are not windows you were trying to reach.

04

Raise, don't rearrange

Focusing moves nothing. The owning app is activated and the matching window is raised — the geometry is left exactly as it was.

Grid mode

And when you do want the grid.

/ flips every key you already know onto a 12 × 8 grid. A nudge moves one cell instead of 80 points, a resize adds a cell, and every edge rounds onto the nearest line — so windows built in grid mode are aligned to each other whether you were paying attention or not.

It is a mode, not a mood: the menu bar item picks up a so you can see which one you are in from across the room, and / puts it back. Try it on the desktop at the top of this page.

  • Move steps by one cell in the direction you pressed
  • Grow and shrink work in whole cells too
  • Snapping rounds the fraction to whole columns or rows
  • Padding is ignored — cells meet edge to edge

12 columns, 8 rows — both are lines in the config file, so a 16 × 9 desk or a 6 × 4 one is a restart away.

On a real desk

Twenty-five seconds, no narration.

Everything above is drawn. This is not — it is a screen recording of the app moving real windows around a real Mac.

The interface

Five items and a shrug.

Shaka is an LSUIElement, so it has no dock icon and no window of its own. Everything it can be asked to do is in this menu, and most days you will open it to turn the thing off for a screenshare and turn it back on afterwards.

Shortcuts
Every binding as it is currently configured — the leader is spelled with whatever key you set.
Enabled
Disables the event tap without quitting. Your keys go back to whatever they meant before.
Edit Config…
Opens config.toml in TextEdit, because that is a thing every Mac has.
Reload Config
Re-reads the file, rebuilds the bindings and the menu. No restart, no relaunch.
Quit Shaka
Tears the tap down and exits.
🤙
Shaka
Shortcuts
Enabled⌘E
Edit Config...⌘,
Reload Config⌘R
Quit Shaka⌘Q
⌃ ←→↑↓Focus window
⌃⌥ ←→↑↓Move window
⌃⇧ ←→↑↓Resize window
⌃⌘ ←→↑↓Snap to half
⌃ ReturnCenter window
⌃⇧ ReturnFill screen
⌃ /Toggle grid mode

Configuration

One file, written for you on first launch.

~/.config/shaka/config.toml appears the first time Shaka runs, commented, with every default in it. Change the leader to "opt" and the Mission Control collision disappears; rebind any action to any key you like; hit Reload Config and it is live.

# Shaka Window Manager Configuration
# Restart Shaka or click "Reload Config" after editing.
#
# Leader key options: "ctrl", "opt" / "alt", "cmd", "shift"
#
# Key names for bindings:
#   Arrows:  left, right, up, down
#   Special: return, space, tab, escape, delete
#   Letters: a-z
#   Numbers: 0-9
#   Combo example: "leader+shift+left"

leader = "ctrl"

move_step = 80
resize_step = 80
edge_snap = 20
screen_padding = 10

animation_stiffness = 300
animation_damping = 28

grid_columns = 12
grid_rows = 8

[bindings]
focus_left    = "leader+left"
focus_right   = "leader+right"
focus_up      = "leader+up"
focus_down    = "leader+down"
move_left     = "leader+opt+left"
move_right    = "leader+opt+right"
move_up       = "leader+opt+up"
move_down     = "leader+opt+down"
grow_width    = "leader+shift+right"
shrink_width  = "leader+shift+left"
grow_height   = "leader+shift+up"
shrink_height = "leader+shift+down"
snap_left     = "leader+cmd+left"
snap_right    = "leader+cmd+right"
snap_up       = "leader+cmd+up"
snap_down     = "leader+cmd+down"
center        = "leader+return"
fill          = "leader+shift+return"
toggle_mode   = "leader+/"
leaderThe modifier every shortcut starts with. "opt", "cmd" and "shift" also work.
move_stepPoints a nudge travels.
resize_stepPoints a grow or shrink adds, split either side of the centre.
edge_snapHow close to an edge a nudge has to land before it sticks to it.
screen_paddingThe gap left around a snapped or filled window.
animation_stiffnessSpring constant. Higher gets there sooner.
animation_dampingFriction. Lower overshoots and wobbles back.
grid_columnsColumns in grid mode.
grid_rowsRows in grid mode.

Key names a binding will accept

Arrowsleft · right · up · down
Specialreturn · space · tab · escape · delete
Lettersa – z
Numbers0 – 9
Punctuation- = [ ] ; ' , . / \ `
Modifiersctrl · shift · cmd · opt / alt · leader

Join them with +. The word leader stands in for whatever leader is set to, so rebinding one line changes every shortcut at once.

Reference

All nineteen of them.

The whole surface area of the app, in the order the config file writes it. There is no twentieth thing hiding behind a preference.

focus_leftFocus the window to the left⌃ ←
focus_rightFocus the window to the right⌃ →
focus_upFocus the window above⌃ ↑
focus_downFocus the window below⌃ ↓
move_leftNudge it left⌃⌥ ←
move_rightNudge it right⌃⌥ →
move_upNudge it up⌃⌥ ↑
move_downNudge it down⌃⌥ ↓
grow_widthWider, from the centre⌃⇧ →
shrink_widthNarrower, from the centre⌃⇧ ←
grow_heightTaller, from the centre⌃⇧ ↑
shrink_heightShorter, from the centre⌃⇧ ↓
snap_leftSnap to the left ½ → ⅓ → ⅔⌃⌘ ←
snap_rightSnap to the right ½ → ⅓ → ⅔⌃⌘ →
snap_upSnap to the top ½ → ⅓ → ⅔⌃⌘ ↑
snap_downSnap to the bottom ½ → ⅓ → ⅔⌃⌘ ↓
centerCentre it where it is⌃ ↩
fillFill the screen, minus the padding⌃⇧ ↩
toggle_modeSwitch between nudging and the grid⌃ /

How it works

Seven system calls, no daemon.

A keyboard tap at the top of the event stream, the Accessibility API for reading and writing window frames, and the window list for finding neighbours. There is no helper tool, no login item, no launch agent and nothing listening on a port.

The tap sees every keystroke — that is what a tap is — and swallows only the combos in your config, passing everything else straight through. If macOS ever disables the tap for taking too long, Shaka notices and switches it back on rather than quietly dying.

On first launch it asks for Accessibility, then polls once a second until you grant it, so the shortcuts start working the moment you tick the box — no relaunch.

Hears the keysCGEvent.tapCreate(tap: .cghidEventTap)
Finds your windowAXUIElementCopyAttributeValue(kAXFocusedWindowAttribute)
Moves and sizes itAXUIElementSetAttributeValue(kAXPosition, kAXSize)
Finds the neighboursCGWindowListCopyWindowInfo(.optionOnScreenOnly)
Raises oneAXUIElementPerformAction(kAXRaiseAction)
Knows the displaysNSScreen.screens[…].visibleFrame
Asks permissionAXIsProcessTrustedWithOptions(kAXTrustedCheckOptionPrompt)

Fine print

What it will not do.

Better to read this now than to find out after the install.

It is not signed or notarized

The installer clones the repo and runs swift build -c release on your machine, which is why there is no Gatekeeper dance and why you need the Xcode Command Line Tools.

⌃ + arrows is already taken

macOS gives those to Mission Control. Turn them off in Keyboard ▸ Keyboard Shortcuts ▸ Mission Control, or set leader = "opt" and never think about it again.

It needs Accessibility

There is no way to move another app's window without it. Shaka swallows only the combos in your config — every other key passes straight through the tap untouched.

It does not do Spaces

Windows move within the display they are on. Sending one to another monitor or another Space is still a drag or a Mission Control gesture.

It remembers nothing

No saved layouts, no per-app rules, no restore on login, no auto-arrange when a monitor appears. You press keys, windows move. That is the whole contract.

Some windows say no

A window with a minimum size will refuse to shrink past it, and anything that does not talk to the Accessibility API — the odd game, the odd Electron shell — simply will not budge.

The TOML parser is small

About sixty lines: key = value, [sections], # comments, strings, numbers and booleans. Arrays, tables and multi-line strings are not part of the deal.

There is no window

Shaka is LSUIElement, so there is no dock icon and nothing to ⌘-Tab to. The 🤙 in the menu bar is the entire interface, and Quit is the bottom item.

Go move a window.

The script clones the repo, builds it with Swift Package Manager and puts Shaka.app in /Applications. No tap, no DMG, nothing to drag.

$curl -fsSL https://raw.githubusercontent.com/wes/shaka/main/install.sh | bash

macOS 13+ · Xcode Command Line Tools

01

Run the line

Two or three minutes, most of it Swift compiling. It prints what it is doing.

02

Launch it

From Spotlight or /Applications. The 🤙 appears in the menu bar; nothing appears in the dock.

03

Tick the box

System Settings ▸ Privacy & Security ▸ Accessibility ▸ Shaka. The shortcuts start working straight away.

Prefer to look first? git clone the repo and run make install. Changed your mind? rm -rf /Applications/Shaka.app ~/.config/shaka and remove it from Accessibility.