A macOS menubar app that applies game design principles — variable reinforcement, sensory differentiation, rarity coloring — to developer notifications. Built in one afternoon with Claude Code.
Share this post:
Export:


It started with a sound effect.
I was looking for the Diablo ring discovery SFX — that unmistakable chime when you find something rare. I knew I had it somewhere on my Mac. I’d used it years ago in Million on Mars for when players struck a valuable ore vein. But where was the file?
So I asked Claude Code to help me find it. We searched my entire machine — Spotlight queries, audio file scans, regex through game asset directories. Nothing named “diablo” or “ring” turned up as audio. Then I remembered: I’d renamed it. It was for finding ore, not rings.
That narrowed it down. Deep in the Million on Mars Unity project: OreDiscovery.mp3. Buried in ~/Desktop/GreaterMoM/SMS/MarsSim/assets/sounds/. I played it and there it was — that perfect ascending chime that means you found something good.
And then the question that started everything: what events in my daily workflow deserve this sound?
Plenty of people are building these now. If you run multiple Claude Code sessions in parallel, you already know the deal: one terminal needs your approval, another just finished a build, and you’re staring at the wrong window. It’s the universal complaint.
I run 3-5 sessions simultaneously — CFO repo, main Bike4Mind app, blog content, prototypes. macOS notifications stack up and become noise. So I built my own orchestrator. The twist is that mine has game design baked in, because that’s what I’ve been doing for 25 years.
I needed something that:
That last one is the hard part. And it’s where the game design comes in.

LootDrop is a native macOS menubar app. Diamond icon in the menu bar. Click it, see your event feed. Any tool or script can POST a JSON event to localhost:7777 and it appears in the feed with a sound.
Here’s the entire technology stack:
The whole thing is ~600 lines of code across 9 files. It builds with swift build from the command line. No Xcode project needed.
I built it with Claude Code in a single afternoon. Not a prototype — the production version. The one running in my menu bar right now as I write this.
The core is dead simple. NWListener gives you a TCP server in about 40 lines:
let params = NWParameters.tcp
listener = try NWListener(using: params, on: NWEndpoint.Port(rawValue: port)!)
listener?.newConnectionHandler = { connection in
self.handleConnection(connection)
}
listener?.start(queue: .global(qos: .userInitiated))
Two endpoints: GET /health and POST /event. That’s it. The event payload is just JSON:
{
"type": "pr_merged",
"title": "PR #42 merged",
"subtitle": "bike4mind repo",
"rarity": "legendary",
"source": "myproject",
"behavior": "flash"
}
Any script, hook, or webhook can fire events. curl is the universal client.
Events have one of three behaviors, each with a different notification pattern:
| Behavior | When | What Happens |
|---|---|---|
| flash | Task complete | Popover opens for 4 seconds, then closes itself |
| persist | Needs your input | Popover opens and stays until you dismiss it |
| silent | Background event | Sound plays, badge updates, no popover |
The silent behavior is where the Diablo ring sound lives. Context compaction in Claude Code — something you can’t act on, but it’s rare enough to warrant the good sound. You hear it and think something just happened but you don’t need to do anything. Like hearing a legendary drop in the next room.

Here’s where it gets interesting. I’ve been making games since 1997. Starfleet Command at Taldren. GoPets, which we sold to Zynga. At Zynga I was GM — half the FarmVille team, ran Mafia Wars, did $60M in cross-network installs. Then Million on Mars. After 25+ years of designing feedback loops, reward systems, and attention management — these principles are automatic for me.
Developer tools almost completely ignore game design. Think about it: your CI/CD pipeline either shows a green checkmark or a red X. Your terminal either beeps or it doesn’t. There’s no nuance. No feeling. No psychological sophistication at all.
Games figured this out decades ago. Here are three principles I put into LootDrop:
This is the big one. In Diablo, you don’t know when the next legendary will drop. That unpredictability is what keeps you playing. If a legendary dropped every 100 kills exactly, you’d get bored. The randomness creates anticipation.
In LootDrop, each notification behavior has two sounds: a primary and a rare. You configure the chance — 5%, 10%, 20%. Most of the time when a task completes, you hear the normal Bottle sound. But occasionally — randomly — you hear the rare variant instead.
struct BehaviorSoundConfig: Codable {
var primarySound: String
var rareSound: String
var rareChancePercent: Int
func rollSound() -> String {
if rareChancePercent > 0 && Int.random(in: 1...100) <= rareChancePercent {
return rareSound
}
return primarySound
}
}
Seven lines of code. But those seven lines are the difference between a notification system you mute after a week and one that still feels alive months later.
This is called variable ratio reinforcement in behavioral psychology. It’s the same mechanism behind slot machines, loot boxes, and fishing. The uncertain reward keeps the dopamine flowing. Applied to developer notifications, it means the sounds never become wallpaper.
In fighting games, light attacks and heavy attacks have distinct sounds. You learn to read the game by ear before your eyes process the visual. A skilled Street Fighter player hears the difference between a jab and a roundhouse without looking.
LootDrop does the same thing. Each behavior has its own sound:
After a day of use, you stop reading notifications. You hear them. “That was a permission request from the CFO repo” — without looking. Your ears become a monitoring dashboard for your parallel work sessions.
World of Warcraft established the color tier system that every game since has copied:
LootDrop uses the same colors for event dots in the feed. A legendary event (orange dot) catches your eye before you read the text. An epic event (purple) stands out from the common gray noise. This makes the event feed scannable at a glance — you don’t read it sequentially, you scan for color.
This feature came from real pain. I’d start a Claude Code session in one terminal, switch to another, and completely forget the first one existed. It would sit there for 30 minutes waiting for me to approve a file edit. Total waste.
The drift detector tracks persist events — things waiting for my approval. If 20 minutes pass without action, LootDrop plays a reminder sound and pops open: “Forgotten session (20m) — Still waiting for approval.”
class DriftDetector {
private var pendingSources: [String: Date] = [:]
func trackPersist(source: String) {
if pendingSources[source] == nil {
pendingSources[source] = Date()
}
}
func clearSource(source: String) {
pendingSources.removeValue(forKey: source)
}
}
Simple timer. Huge quality-of-life improvement. It’s the developer equivalent of a restaurant buzzer that vibrates when your food is ready — except it also reminds you if you forgot to pick up your food.
Every event carries a source field — typically the project directory name. When you tap an event row in the popover, LootDrop:
AppleScript isn’t elegant, but it works. LootDrop tells System Events to find the Ghostty window matching your source string and performs an AXRaise action on it. One tap takes you from “what just happened?” to the exact terminal that needs attention.
LootDrop hooks into Claude Code’s notification system. Three hooks in ~/.claude/settings.json cover the main events:
persist behavior (stays open until you act)silent behavior (Diablo ring, no popup)flash behavior (brief popup, then gone)The hooks are just curl commands that fire in the background. If LootDrop isn’t running, the curl fails silently. Zero impact on your workflow.
Events are saved to ~/.config/lootdrop/history.json with configurable retention (3-30 days). This turned out to be more useful than expected.
When I sit down in the morning, I click the diamond icon and see yesterday’s events — dimmed but still there. “Oh right, I was in the middle of that expense report refactor.” The event feed becomes a breadcrumb trail of my work sessions.
There’s a one-click export button that dumps today’s events as a markdown file grouped by project. Instant standup notes.
Here’s what I think most developer tool builders are missing:
Game designers have spent decades perfecting attention management and feedback loops. Developer tools mostly ignore this entire body of knowledge.
We have 50 years of research on how to keep humans engaged, how to prevent fatigue, how to make feedback feel rewarding rather than annoying. Game designers use variable reinforcement, sensory differentiation, progressive disclosure, achievement systems, spatial audio, and dozens of other techniques — all refined through millions of hours of playtesting.
Developer tools use a beep.
There’s a massive untapped space where game design principles meet developer experience. LootDrop is one small example. But imagine if your IDE used achievement sounds for milestone commits. If your CI pipeline had a combo counter for consecutive passing builds. If your code review tool used rarity coloring for change significance.
This isn’t gamification in the corporate “add points and badges” sense. That’s cargo cult game design. This is applying the underlying psychology — the stuff that actually works — to tools we use every day.
LootDrop is open source, MIT licensed. ~600 lines of Swift, zero dependencies, builds on any Mac with Xcode command line tools.
github.com/MillionOnMars/lootdrop
git clone https://github.com/MillionOnMars/lootdrop.git
cd lootdrop
./build.sh
It was built in a single afternoon, pair-programming with Claude Code. Every line of Swift was written in the conversation. The sound effect that started it all — OreDiscovery.mp3 from Million on Mars — is still there in my custom sounds folder, waiting for rare events that deserve it.
Some sounds are too good to waste on routine notifications. That’s the whole point.
Get notified when I publish new blog posts about game development, AI, entrepreneurship, and technology. No spam, unsubscribe anytime.
Loading comments...
Published: March 12, 2026 8:01 PM
Last updated: March 12, 2026 10:29 PM
Post ID: f39c223e-17c7-41a1-8c97-fe596695407a