Show HN: SpinWin - A macOS menu bar app to visually rotate or spin any window
How It Works
A macOS menubar utility that visually rotates a selected window by any angle (90ยฐ increments, arbitrary angles, or a continuous spin).
macOS has no public API to rotate another app's window. Real rotation would require private SkyLight calls (CGSSetWindowTransform) with System Integrity Protection disabled - not worth it. Instead, SpinWin fakes it:
- Hide the selected window off-screen via the Accessibility API. It keeps rendering there, so it can still be captured.
- Capture its contents continuously with ScreenCaptureKit (
SCStream+desktopIndependentWindowfilter) - this grabs only that window's buffer, so the overlay never mirrors itself. - Draw the frames in a transparent, borderless overlay window placed where the original was, rotated with a
CALayertransform.
Because it's a layer transform, any angle works - the overlay is sized to the rotated content's bounding box so nothing clips, and a diagonal-sized square is used while spinning.
Installation & Building
The easiest way to get SpinWin is the latest release - the download is signed with a Developer ID and notarized by Apple, so it launches without Gatekeeper warnings.
Or build it yourself:
./scripts/build-app.sh # builds SpinWin.app (release)
open SpinWin.app # grant Screen Recording + Accessibility
For quick iteration during development:
swift build && swift run SpinWin
Usage
Left-click the menubar icon to start: choose a rotation in the bottom options bar (a preset angle, a spin speed + direction, or free rotation), then click the window you want.
Drag the overlay to reposition it; drag its handle to rotate freely. Press Esc on the overlay to stop and restore the window.
Right-click (or Control-click) the icon for the menu: active rotations (with per-window angle/spin controls), Stop all, and Quit.
Required Permissions
- Screen Recording - to capture the window contents.
- Accessibility - to move the source window off-screen and restore it.
Caveats / Limitations
- The overlay is a live picture, not interactive. You can drag and rotate it, but clicks and keystrokes are not sent to the real window underneath.
- Mission Control / Exposรฉ leak. The hidden window is parked off-screen but still a real window, so Exposรฉ shows it unrotated. There is no public API to exclude another app's window from Mission Control, and minimizing it would stop the capture.
- Some apps throttle rendering of off-screen windows, which can slow the feed.
- A window that resizes itself while rotated keeps its original footprint. You can't drag-resize the hidden window, but an app can still resize it on its own (a video player adapting to a new track, a terminal responding to an escape sequence). The capture is letterboxed into the original size until you stop and rotate it again.
Files
| File | Role |
|---|---|
main.swift |
App entry point (accessory/menubar policy) |
AppDelegate.swift |
Menubar item; left-click rotates, right-click opens menu |
Permissions.swift |
Requests Screen Recording + Accessibility at launch |
WindowPicker.swift |
Full-screen window picker + rotation options bar |
RotationManager.swift |
Tracks one rotation session per window |
RotationSession.swift |
Orchestrates hide โ capture โ overlay per window |
CaptureEngine.swift |
ScreenCaptureKit stream โ IOSurface frames |
OverlayWindow.swift |
Transparent rotating/spinning overlay, drag |
AccessibilityWindowMover.swift |
Move source window off-screen + restore |
ActivationChoice.swift |
Initial rotation choice (angle/spin/free) |
SpinDirection.swift |
Clockwise/counterclockwise spin option |
MenuBarIcon.swift |
Draws the menubar icon |
Comments
No comments yet. Start the discussion.