Building WindowPin: Picture-in-Picture for Everything

It started, as most of my projects do, with a minor annoyance that I refused to accept.

I was referencing a design spec in one window while writing code in another. The spec kept disappearing behind my editor every time I clicked into it. I tried split screen. I tried tiling. I tried the noble art of constantly pressing commandtab and remembering which paragraph I was looking at. None of it was good enough.

What I actually wanted was picture-in-picture — but not just for video. For any window. A floating, always-on-top overlay that shows a live view of whatever window I choose, sitting on top of my workspace while I get on with actual work.

So I built one.

The Pitch

WindowPin lives in your menu bar. Press a keyboard shortcut (controlcommandP by default), and the frontmost window becomes a floating overlay. It stays on top, updates in near-real-time, and you can drag it wherever you like. Pin a Slack conversation, a dashboard, a Figma artboard, a terminal running a build — whatever you need to keep an eye on while you work.

The capture rate is configurable: 0.5 FPS for static reference material, up to 30 FPS if you’re monitoring something that moves.

The Rabbit Hole

“Capture a window and display it on top” sounds simple. It is not.

Finding the Window

The first challenge is identifying which window to pin. macOS doesn’t hand you a tidy list of windows with labels. You get a grab bag of CGWindowList entries — some visible, some offscreen, some belonging to the system — and you have to figure out which one the user is looking at.

WindowPin uses a three-stage matching strategy:

  1. Ask the Accessibility API for the frontmost window’s title and PID
  2. Try to match that against CGWindowList entries by PID and exact title
  3. If that fails (and it does — some apps use different titles in different APIs), fall back to matching by PID and frame bounds within a 2-pixel tolerance
  4. Last resort: just grab the first layer-0 window for that process

This layered approach exists because I kept finding apps that broke simpler matching. It turns out “which window is in front” is a surprisingly philosophical question on macOS.

Capturing Content

For the actual screen capture, I’m using ScreenCaptureKit — specifically SCScreenshotManager.captureImage(). It’s efficient and doesn’t require the deprecated CGWindowListCreateImage approach. But it does require Screen Recording permission, which macOS prompts for automatically. Without it, your pinned overlays show nothing — just blank panels floating serenely above your workspace.

The Z-Level Trick

Here’s a detail I’m quite pleased with: the overlays never actually hide. They change z-level.

When a pinned window should be visible, its overlay sits at NSWindow.Level.floating — above everything. When you switch to the actual app whose window is pinned (why would you need the overlay when you’re looking at the real thing?), the overlay drops to a level below normal windows. No fade. No show/hide toggle. Just a quiet z-level swap.

This is faster, avoids flicker, and handles edge cases better than the show/hide approach I tried first.

The Global Shortcut Problem

SwiftUI has no concept of a global keyboard shortcut — one that works when your app isn’t in focus. For that, you need a CGEvent tap: a low-level event interceptor that runs at the system level and can observe (or consume) keyboard events before they reach any application.

The implementation is a C-compatible callback function accessing module-level globals. It’s not pretty. It can’t be pretty — this is one of the few places where Swift’s safety guarantees have to step aside and let you talk to the system directly. The tap also has a habit of being disabled by macOS if it takes too long to process events, so there’s a polling mechanism to re-enable it.

And of course, it requires Accessibility permission. Which the user has to grant manually. Which they can’t grant until the app has launched and tried to register the tap. Classic chicken-and-egg, solved with a retry loop and a polite prompt.

Architecture

The app breaks down into five clean pieces:

No Xcode project. Just Swift Package Manager with a build script that assembles the .app bundle manually. The icon is generated programmatically in Swift — a gradient blue pushpin — because I find it easier to tweak code than fiddle with image editors.

Coordinate Systems, Because of Course

One detail that caught me: CGWindowList uses top-left origin coordinates (like most graphics APIs). AppKit uses bottom-left origin (because it was 1988 and NeXT thought that was a good idea). Every time the overlay syncs its position to the target window, it has to flip the Y axis:

nsY = screenHeight - cgY - windowHeight

It’s a one-liner. It took me longer than I’d like to admit to figure out why my overlays were appearing in mirror-world.

The Smart Auto-Layer Behaviour

The feature I’m most proud of is the automatic layering. When you activate the app whose window is pinned, the overlay drops behind the real window. When you switch to any other app, it floats back up.

This means the overlay is only visible when it’s useful — when you’re looking at something else and need the pinned content as a reference. The moment you switch to that app, the overlay gets out of the way. It’s the kind of behaviour that’s invisible when it works, which is exactly the point.

The implementation uses NSWorkspace notifications for app activation events, checks whether the activated app owns any pinned windows, and adjusts z-levels accordingly.

What I’d Do Differently

Not much, honestly. This one came together relatively cleanly. If I were starting over, I’d probably build the shortcut customisation UI in SwiftUI rather than the NSEvent-based keyboard listener I used — it works, but it’s more code than it needs to be.

I’d also consider using ScreenCaptureKit’s streaming API instead of repeated screenshots. It would be more efficient at higher frame rates, but the screenshot approach is simpler and works well enough for the use case.

Try It

WindowPin is free, open source, and released to the public domain.

WindowPin on GitHub · Product page