New in 1.1.0Reef mode 👊 — Hyprland-style tiling
Shaka is a hand in your menu bar and a handful of keys under your fingers. Flow nudges windows around on a spring and enforces nothing. Reef packs every one of them into a tiling layout with no overlap. ⌃ / swaps between them, and coming back puts every window exactly where it was.
$curl -fsSL https://raw.githubusercontent.com/wes/shaka/main/install.sh | bashFree · MIT · macOS 13+ · Xcode Command Line Tools
~/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 ▌
That desktop is drawn, not screenshotted, and the windows are not on CSS transitions. Every move, resize and snap runs Shaka's own geometry, and switching it to Reef runs Shaka's own dwindle tree — the same recursion, the same gaps — integrated by Shaka's own spring at the same fixed 1/120 step the app uses. The overshoot you feel here is the overshoot you get at animation_damping = 28.
Two modes
Most people want a tiler right up until they want to drag a window somewhere silly. Shaka stopped arguing about it and shipped both, on one set of bindings, with a single keystroke in between — and the menu bar tells you which one you are in from across the room.
Free-floating
Windows go where you put them. Nudge, resize, centre, fill and snap, on a spring, with nothing enforced and nothing to lay out.
Dwindle tiling
Every window becomes a tile in a binary tree that fills the screen. Nothing overlaps, everything is reachable, and one keystroke lifts it all back off.
⌃⌥ ← nudges a window in Flow and swaps two tiles in Reef. Nothing to relearn when you switch — the verb changes, the finger does not.
It is Hyprland's dwindle layout, down to the names: togglesplit, resizeactive, gaps_in and gaps_out. If you have used one on Linux, you already know this one.
Cocoa, the Accessibility API, a binary tree 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.
Reef 👊
Entering Reef takes every tileable window on every display, writes down where it was, and packs it into a binary tree that fills the screen exactly. Each new window splits the focused tile in half along that tile's long axis, so the layout spirals inward the way dwindle does and the halves stay near-square instead of degenerating into columns.
Press ⌃ / again and every window goes back to the frame Reef found it in, on the same spring. That is the whole pitch: the structure is something you drop onto your screen for an afternoon and lift off again, not something you commit your desktop to.
One window fills the screen.
A is wider than tall, so it splits side by side.
B is now taller than wide, so C stacks under it.
C is wide again — D takes its right half.
Nobody chose those four rectangles. Each one is what host.rect.width >= host.rect.height decided when the window opened, which is why the shape is predictable even though you never picked a layout.
Every tileable window on every display has its frame written down before a single tile is laid, which is what makes leaving lossless rather than approximate.
Each screen gets its own dwindle tree filling its visible frame. Run out of tiles pressing a direction and focus carries on onto the next display.
An Accessibility observer per app watches windows appear, close, minimise and come back; a five-second timer covers apps that report nothing. Bursts are coalesced into one diff.
Every saved frame is handed back to its window on the same spring, so Reef is something you drop onto the screen and lift off again — not something you commit to.
Reef adds the actions a tiler needs and a floating manager has no use for. In Flow their bindings are left unbound rather than made inert — the tap passes the keystroke through to whatever app you are in, so ⌃⇧S stays yours until the moment you want it.
gaps_out = 12 at the screen edge and gaps_in = 6 around each tile, so two neighbours sit 12 points apart — the same arithmetic Hyprland does. Inset the screen by gaps_out − gaps_in before splitting, inset each leaf by gaps_in after, and the outer margin and the gutters come out equal without a special case.
The dispatcher each Reef action is a reading of.
movefocusNearest tile in the direction, scored off the laid-out rects.
movewindowThe two leaves exchange windows; the tree shape does not move.
resizeactiveWalks up to the nearest split on that axis and nudges its ratio.
togglesplitTurns a side-by-side pair into a stacked one and back.
togglefloatingThe tile leaves the tree and gets Flow behaviour at 60% of the screen.
fullscreenOne tile covers the whole visible frame, gaps and all.
movewindow mon:<dir>Removed from one tree and inserted into the next display's.
Motion
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.
Both modes share one animator, which is what makes switching feel like a decision rather than a redraw: every tile springs into place at once, each with its own velocity, off a single timer that drops to 16ms once more than two windows are moving so the Accessibility round trips have room to land.
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.
The animation timer is created for a move and cancelled the moment the last window settles. Between keystrokes Flow is an event tap and a menu, and that is all.
This page's demo checks prefers-reduced-motion and jumps straight to the target instead of springing.
Flow 🤙 · Snapping
⌃ ⌘ ← 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. In Reef the same key means something else entirely — it hands the tile to the next display.
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.
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.
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.
⌃ ⇧ ↩ fills the visible frame minus the padding. No green button, no Space of its own, no animation you have to sit through.
Focus
⌘-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.
Flow reads the on-screen window list in a single call, so it sees every app's windows rather than only its own. Reef does not need to ask: it already has every tile's rectangle, so it scores them straight off the tree — and runs off the edge onto the next display when there is no tile left in that direction.
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.
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.
Standard windows, not minimised, whose position and size the app will actually let you set. Sheets, palettes, popovers and fixed-size dialogs are not windows you were trying to reach.
Focusing moves nothing. The owning app is activated and the matching window is raised — the geometry is left exactly as it was.
On a real desk
Everything above is drawn. This is not — it is a screen recording of the app moving real windows around a real Mac. It predates Reef, so what you are watching is Flow: nudging, snapping and filling, at the speed you actually use it.
The interface
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 switch modes with the mouse instead of the keyboard, or to turn the thing off for a screenshare and turn it back on afterwards.
The glyph is the status: 🤙 for Flow, 👊 for Reef, and 🤙⚠ when Accessibility has been revoked — at which point the menu drops everything except an explanation and a button that opens the right settings pane.
Configuration
~/.config/shaka/config.toml appears the first time Shaka runs, commented, with every default in it and the two modes' settings under their own headings. Change the leader to "opt" and the Mission Control collision disappears; set default_mode = "reef" and it tiles from launch; rebind any action to any key you like; hit Reload Config and it is live.
A binding you leave out falls back to the default rather than disappearing, so a config written before Reef existed picks up all three of its new actions on the next launch instead of quietly losing them.
# 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"
# Which mode to start in: "flow" (free-floating) or "reef" (tiling)
default_mode = "flow"
# --- Flow mode ---
move_step = 80
resize_step = 80
edge_snap = 20
screen_padding = 10
# --- Reef mode ---
# gaps_out: space at the screen edge
# gaps_in: space around each tile, so neighbours sit 2x this apart
gaps_in = 6
gaps_out = 12
# Spring animation parameters (both modes)
animation_stiffness = 300
animation_damping = 28
[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+/"
toggle_split = "leader+shift+s"
toggle_float = "leader+shift+f"
cycle_next = "leader+opt+tab"
Join them with +. The word leader stands in for whatever leader is set to, so rebinding one line changes every shortcut at once.
Reference
The whole surface area of the app, in the order the config file writes it. One column of keys, because there is only one column of keys — the last three are Reef's, and in Flow they are not bound at all.
How it works
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.
Reef adds the one thing Flow never needed: an Accessibility observer on every running app, so a window opening or closing is a notification rather than a poll. The signal is deliberately coarse — individual notifications are unreliable across apps, so any event triggers a full re-scan and diff, coalesced over 120ms, with a five-second timer behind it for apps that post nothing at all. Every AX request is capped at a quarter of a second, so one unresponsive app cannot stall the tap.
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.
CGEvent.tapCreate(tap: .cghidEventTap)AXUIElementCopyAttributeValue(kAXFocusedWindowAttribute)AXUIElementSetAttributeValue(kAXPosition, kAXSize)CGWindowListCopyWindowInfo(.optionOnScreenOnly)AXUIElementPerformAction(kAXRaiseAction)AXObserverCreate(pid) + kAXWindowCreatedNotificationAXUIElementSetMessagingTimeout(systemWide, 0.25)NSScreen.screens[…].visibleFrameAXIsProcessTrustedWithOptions(kAXTrustedCheckOptionPrompt)Fine print
Better to read this now than to find out after the install.
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.
Shaka is ad-hoc signed, so a rebuild changes its signature and macOS quietly revokes the grant while still showing it ticked. Remove it from the list and add it back, or run tccutil reset Accessibility com.wes.shaka. The menu bar shows 🤙⚠ when this has happened.
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.
The dwindle layout is the only one implemented — Hyprland's master layout is not there. It is honest about that rather than half-doing it.
Drag or resize a tiled window by hand and the tile keeps the frame it was assigned; the next layout pass puts it back. Reef is driven from the keyboard.
Windows move within the display they are on, and each display gets one Reef tree rather than one per Space. Sending a window to another Space is still a Mission Control gesture.
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. In Reef, ⌃⇧ F floats those out of the way.
No saved layouts, no per-app rules, no restore on login. You press keys, windows move. Reef is the only structure on offer, and it lasts exactly as long as you leave it on.
About sixty lines: key = value, [sections], # comments, strings, numbers and booleans. Arrays, tables and multi-line strings are not part of the deal.
Shaka is LSUIElement, so there is no dock icon and nothing to ⌘-Tab to. The menu bar item is the entire interface, and it tells you which mode you are in: 🤙 or 👊.
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 | bashmacOS 13+ · Xcode Command Line Tools
Two or three minutes, most of it Swift compiling. It prints what it is doing, and finishes with the two-mode shortcut table.
From Spotlight or /Applications. The 🤙 appears in the menu bar; nothing appears in the dock.
System Settings ▸ Privacy & Security ▸ Accessibility ▸ Shaka. The shortcuts start working straight away — no relaunch.
Prefer to look first? git clone the repo and run make install. Updating? Re-grant Accessibility afterwards — the ad-hoc signature changes with every build. Changed your mind? rm -rf /Applications/Shaka.app ~/.config/shaka and remove it from Accessibility.