CreativeNotch

Turns the MacBook notch into something useful, without the battery drain.

All projects
See CreativeNotch in action, from music controls to clipboard search and everyday utilities.
The open panel on the timer tab. Tabs run along the top, a music column with album art, a progress bar and playback controls sits on the left, and three countdowns run on the right, with 24, 4 and 9 minutes left, each with its own progress bar, pause button and close button.The open panel on the battery tab. The music column is on the left, and the right shows a green battery icon with a charging bolt at 84 percent, the word Charging, and a Low Power Mode switch set to off.
Music controls, countdown timers and battery status in CreativeNotch.
Problem
A notch app runs all day, but you hardly ever look at it. So the only cost that really matters is what it uses while it sits there doing nothing.
Decision
I set one rule before writing any features, and enforced it in one place. Nothing runs unless something needs it. That means no checking on a timer when macOS can send an event instead, no input monitor left running, and no listening to your audio.
Result
Thirteen features, nine of which never ask for a permission, and each one truly stops when you switch it off. The one repeating timer exists only because macOS has no way to say the clipboard changed.
Role
Sole developer
Period
2026 to now
Stack
Swift, SwiftUI, macOS 26+

Checked against the source

2,367
tests passing in CICI run 40175097659
1
repeating Timer in the app sourceClipboardPoller.swift, line 36
1
global event monitor, installed only while the panel is openAppDelegate.swift, line 702
0
event taps, so nothing asks for Accessibilitycode search
v0.14.0
latest releaseGitHub releases

Each figure is read from the page it links to, not typed in. Source figures are at commit 4b56a55. Checked .

How it started

My brother wanted a personalised MacBook notch. I started building CreativeNotch for that, and now I use it throughout my own work. I wanted something simple that made an existing part of the screen useful without taking up much of the computer’s resources.

The problem

A notch app lives in the background. It sits under the menu bar the whole time your Mac is on, and you spend almost none of that time looking at it. So the cost that matters is what it uses while idle. The easy way to build one is to keep everything running all the time. You watch the mouse with a global monitor, refresh system stats on a repeating timer, and listen to the audio to drive a visualiser. Each of those is cheap on its own, but none of them ever stops.

The rule

Before I wrote the first feature, I wrote down one rule. Nothing runs when it isn’t needed, and that rule is enforced in one central place instead of being left to each feature. docs/ARCHITECTURE.md bans four things by name. They are a Timer that always runs, a global event monitor that is always installed, checking where the cursor is on a loop, and tapping the audio. The idea behind the list is simple. If you think you need one of these, there is probably a notification you haven’t found yet. There is one animation the rule allows, the moving equaliser in the music view, and it only runs while that view is open.

What the rule decided

It ended up shaping most of the app. Hovering works through an NSTrackingArea on the panel, so nothing runs while your cursor is somewhere else. Battery and Low Power Mode updates arrive through IOPSNotificationCreateRunLoopSource. The keyboard shortcut is registered with Carbon’s RegisterEventHotKey, which hands the key combination to macOS itself, so the app does nothing between presses. The capture light, which tells you when another app is using your camera or microphone, listens for the DeviceIsRunningSomewhere property on audio and video devices instead of asking every few seconds.

The same thinking shows up in the small things. The progress bar for a song has no clock behind it. It runs a single animation to the end of the track and lets Text(timerInterval:) count the time, so nothing ticks while music plays. Trackpad swipes on the notch use the scroll events macOS already sends to the panel, rather than a monitor.

Launch at login comes at the idea from another angle. The switch asks SMAppService for the real answer every time instead of remembering one. If you turn the login item off in System Settings, the app shows it as off, because there’s no saved setting left to disagree with the system.

You don’t have to take my word for any of this, because you can check it in the code. The whole app has exactly one repeating Timer, the clipboard checker, because NSPasteboard really has no change notification. It slows down after two quiet minutes and pauses completely while your screen is locked or your Mac is asleep. There is also exactly one addGlobalMonitorForEvents, and it’s only installed while the panel is open.

What it cost me

A feature that worked. Showing volume and brightness in the notch shipped in v0.2.0, ran for a month, and came out in v0.7.0. Nothing was wrong with it, and I’d fixed its last bug the day before. But it needed a CGEventTap running all the time to catch the media keys. That was the one exception to the rule, and it was the only reason the app asked for Accessibility access. Taking it out removed 3,397 lines and added back 428. Now the rule has no asterisk, and the app asks for nothing when you first open it. Every prompt that’s left comes from something you just did, like opening the camera or switching a feature on.

Getting cleanup right, which was harder than I expected. When nothing polls, every feature works by signing up for events, and every sign up has to be undone when the feature stops. Three features shipped a stop() that missed something start() had set up. The switch said off, the feature looked stopped, and the listener kept running anyway. Reading the code didn’t catch any of them. Counting the active sign ups and testing that count did, so every observer in the app now works that way. A fourth bug turned up in v0.10, and it was the kind a count can’t see. An observer was freed without stop() being called, macOS kept a pointer to it, and the next battery change crashed the app. The callback now holds the observer weakly, so that can’t happen again.

Features I chose not to build. There’s no audio visualiser and there won’t be, because the rule bans tapping the audio by name. When the notch is closed, the now playing badge shows the album cover and four bars that stay still. The bars only move in the music view, which is only on screen while your cursor is over it. An equaliser in the closed notch would keep redrawing for as long as music plays.

Sometimes avoiding a poll costs more than polling would. Since macOS 15.4, only apps with certain signing identities can read what’s playing, and an app signed the way mine is can’t qualify. So the app runs a small helper through the system’s own /usr/bin/perl and reads its answers back as JSON. Running a helper process is heavier than a timer, so it only runs while your Mac is awake and unlocked.

Where it is now

v0.14.0 has thirteen features. They are a file shelf with Quick Look and AirDrop, clipboard history with search and pins, what’s playing, media controls, battery, timers and a stopwatch, a keyboard shortcut, a camera mirror, the capture light, Bluetooth alerts, a calendar glance, a colour picker and a downloads ring. Nine of them need no permission at all. The camera asks the first time you open it. Bluetooth alerts, the calendar and downloads each need a grant, so they ship switched off and ask only when you turn them on. You can switch off any one of them, and switching it off really stops it instead of just hiding it. All of that is handled by one ModuleSwitchboard. The app’s core library only uses Foundation, CoreGraphics and Observation, which means its layout logic, its states and the rules for which view wins can all be tested without a screen. The whole suite, 2,367 tests, runs in CI on every push.