Skip to content

Instantly share code, notes, and snippets.

@firedynasty
Last active May 27, 2026 22:52
Show Gist options
  • Select an option

  • Save firedynasty/32a1d90023e1e1ab4dba055f71305cb8 to your computer and use it in GitHub Desktop.

Select an option

Save firedynasty/32a1d90023e1e1ab4dba055f71305cb8 to your computer and use it in GitHub Desktop.
screenshot then have picture pop up to copy notes

screenshot_float — Always-on-Top Screenshot Overlay for macOS

Take an interactive screenshot and float it as a click-through, always-on-top overlay pinned to the bottom-right corner. Useful for referencing something while you work.

How it works

  1. Cmd+4 triggers screenshot_float.sh — gives you the macOS crosshair to select a region
  2. Select a region — it's displayed as a borderless, transparent, floating window via PyObjC
  3. Press Escape instead — all existing floating screenshots are dismissed
  4. The window is fully click-through — it never steals focus or interferes with your work

One key does everything. Invoke multiple times to stack reference screenshots, cancel to clear them all.

Requirements

  • macOS
  • Python 3 with PyObjC (pip install pyobjc)
  • Karabiner-Elements (for the keyboard shortcuts, or bind however you like)

Files

screenshot_float.sh

#!/bin/bash
# Take interactive screenshot and float it as always-on-top window
# Cancel the selection (Esc) to dismiss all existing floats instead
SCALE="${1:-2.0}"
FILE="/tmp/screenshot_peek_$(date +%s).png"
/usr/sbin/screencapture -i "$FILE"
if [ -s "$FILE" ]; then
    python3 "$HOME/bin/float_image.py" "$FILE" "$SCALE" &
else
    rm -f "$FILE"
    pkill -f "float_image.py" 2>/dev/null
    sleep 0.5
    pkill -9 -f "float_image.py" 2>/dev/null
    rm -f /tmp/screenshot_peek_*.png 2>/dev/null
fi

float_image.py

#!/usr/bin/env python3
"""Float a screenshot as an always-on-top window that auto-dismisses."""

import sys
import os
import signal

import AppKit
import Quartz
from Foundation import NSObject, NSTimer, NSRunLoop, NSDefaultRunLoopMode


def main():
    if len(sys.argv) < 2:
        print("Usage: float_image.py <image_path> [scale] [duration]")
        sys.exit(1)

    image_path = sys.argv[1]
    scale = float(sys.argv[2]) if len(sys.argv) > 2 else 2.0
    duration = float(sys.argv[3]) if len(sys.argv) > 3 else 0  # 0 = no auto-close

    if not os.path.exists(image_path):
        print(f"File not found: {image_path}")
        sys.exit(1)

    app = AppKit.NSApplication.sharedApplication()
    app.setActivationPolicy_(AppKit.NSApplicationActivationPolicyAccessory)

    image = AppKit.NSImage.alloc().initWithContentsOfFile_(image_path)
    if image is None:
        print(f"Could not load image: {image_path}")
        sys.exit(1)

    img_size = image.size()

    # Get screen dimensions first
    screen = AppKit.NSScreen.mainScreen()
    screen_frame = screen.visibleFrame()
    scr_w = screen_frame.size.width
    scr_h = screen_frame.size.height

    # Scale image but cap to bottom-quarter of screen
    max_w = scr_w * 0.75
    max_h = scr_h * 0.35
    win_w = img_size.width * scale
    win_h = img_size.height * scale

    # Shrink proportionally if too large
    if win_w > max_w or win_h > max_h:
        ratio = min(max_w / win_w, max_h / win_h)
        win_w *= ratio
        win_h *= ratio

    # Position at bottom-right corner (macOS y=0 is bottom of screen)
    x = screen_frame.origin.x + scr_w - win_w - 20
    y = screen_frame.origin.y + 20

    frame = ((x, y), (win_w, win_h))

    window = AppKit.NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
        frame,
        AppKit.NSWindowStyleMaskBorderless,
        AppKit.NSBackingStoreBuffered,
        False,
    )
    window.setLevel_(AppKit.NSFloatingWindowLevel)
    window.setOpaque_(False)
    window.setBackgroundColor_(AppKit.NSColor.clearColor())
    window.setHasShadow_(True)
    window.setIgnoresMouseEvents_(True)

    image_view = AppKit.NSImageView.alloc().initWithFrame_(((0, 0), (win_w, win_h)))
    image_view.setImage_(image)
    image_view.setImageScaling_(AppKit.NSImageScaleProportionallyUpOrDown)
    window.contentView().addSubview_(image_view)

    window.makeKeyAndOrderFront_(None)

    # Auto-close after duration (if set)
    if duration > 0:
        def close_app(timer):
            app.terminate_(None)

        NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
            duration, app, "terminate:", None, False
        )

    # Handle SIGTERM/SIGINT gracefully (for kill_floats.sh)
    _should_quit = [False]

    def handle_signal(signum, frame):
        _should_quit[0] = True

    signal.signal(signal.SIGTERM, handle_signal)
    signal.signal(signal.SIGINT, handle_signal)

    # app.run() blocks Python's signal handlers, so poll a flag via NSTimer
    class SignalChecker(NSObject):
        def check_(self, timer):
            if _should_quit[0]:
                app.terminate_(None)

    checker = SignalChecker.alloc().init()
    NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
        0.5, checker, "check:", None, True
    )

    app.run()


if __name__ == "__main__":
    main()

kill_floats.sh (standalone, optional)

The kill logic is now built into screenshot_float.sh (cancel to dismiss), but you can keep this as a standalone script if you want a dedicated kill binding too.

#!/bin/bash
# Kill all floating screenshot windows
pkill -f "float_image.py" 2>/dev/null
sleep 0.5
# Force kill any that didn't exit gracefully
pkill -9 -f "float_image.py" 2>/dev/null
# Clean up temp screenshot files
rm -f /tmp/screenshot_peek_*.png 2>/dev/null

Karabiner-Elements binding (optional)

Add this to your Karabiner rules array — one key does everything:

{
    "from": { "key_code": "4", "modifiers": { "mandatory": ["command"] } },
    "to": [{ "shell_command": "/Users/YOU/bin/screenshot_float.sh" }],
    "type": "basic"
}

Design notes

  • Click-through: setIgnoresMouseEvents_(True) — the overlay never steals focus
  • No dock/menubar presence: runs as NSApplicationActivationPolicyAccessory
  • Stackable: each invocation spawns a new process with a timestamped temp file
  • Graceful shutdown: since app.run() blocks in the ObjC event loop, a 0.5s NSTimer polls a Python flag to handle SIGTERM from the kill script
  • Auto-sizing: scales the image (default 2x) but caps at 75% screen width / 35% screen height
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment