Back to Articles

Spotifree: How a 300ms Polling Loop Muted Spotify Ads on macOS

[ View on GitHub ]

Spotifree: How a 300ms Polling Loop Muted Spotify Ads on macOS

Hook

What if you could detect every Spotify ad by checking a single number every 300 milliseconds? One developer did exactly that, and it worked beautifully—until it didn't.

Context

Before Spotify's widespread Premium adoption, free-tier users endured increasingly aggressive advertising. While browser-based solutions existed, the native Spotify desktop app presented a harder target: a sandboxed application with no official API for ad detection or audio control. Desktop ad-blockers couldn't intercept streaming content, and audio fingerprinting seemed overkill for a simple mute function.

Spotifree emerged as an elegantly minimalist solution to this problem. Rather than analyzing audio streams or attempting network-level blocking, developer Artem Gordinskiy discovered that Spotify's own track metadata exposed advertisements through a simple quirk: ads always reported their track number as 0. This single insight enabled a lightweight menu bar app that could detect ads and mute system audio automatically, all while consuming negligible system resources. The project represents a masterclass in exploiting observable behavior rather than reverse-engineering complex systems.

Technical Insight

Start observing

Query every 0.3s

Get track number

Return track #

Track = 0?

Ad detected: Mute

Normal track: Restore

Update system volume

Menu Bar App

Polling Timer

0.3s interval

AppleScript Bridge

Spotify App

System Audio Control

Volume State Manager

System architecture — auto-generated

Spotifree's architecture centers on three core components: a polling mechanism, an AppleScript bridge to Spotify, and system audio control. The genius lies in how little code was needed to make it work.

The polling loop runs every 0.3 seconds, querying Spotify's current state through macOS's scripting bridge. Here's the conceptual implementation in Swift:

class SpotifyObserver {
    private var timer: Timer?
    private var previousVolume: Float = 0.5
    private let pollInterval: TimeInterval = 0.3
    
    func startObserving() {
        timer = Timer.scheduledTimer(
            timeInterval: pollInterval,
            target: self,
            selector: #selector(checkSpotifyState),
            userInfo: nil,
            repeats: true
        )
    }
    
    @objc private func checkSpotifyState() {
        guard let trackNumber = getSpotifyTrackNumber() else { return }
        
        if trackNumber == 0 {
            // Ad detected - mute audio
            if !isMuted {
                previousVolume = getCurrentVolume()
                setSystemVolume(0)
            }
        } else {
            // Regular track - restore volume
            if isMuted {
                setSystemVolume(previousVolume)
            }
        }
    }
    
    private func getSpotifyTrackNumber() -> Int? {
        let script = "tell application \"Spotify\" to track number of current track"
        var error: NSDictionary?
        let appleScript = NSAppleScript(source: script)
        let result = appleScript?.executeAndReturnError(&error)
        return result?.int32Value.map(Int.init)
    }
}

The AppleScript bridge is where platform-specific knowledge shines. macOS's scripting bridge allows Swift applications to query scriptable applications using a dictionary-based interface. Spotifree doesn't need Spotify's source code or any private APIs—it simply asks Spotify what track is playing through the same interface that power users can access from Script Editor.

The track number heuristic is brilliant in its simplicity. Spotify's internal architecture differentiates between user content (songs with track numbers) and injected content (ads without album context). While this wasn't documented or guaranteed behavior, it proved remarkably stable across multiple Spotify versions. This approach completely bypasses the complexity of audio analysis or network traffic inspection.

For audio control, Spotifree had two modes: muting system volume entirely or targeting Spotify's process specifically. The latter required interfacing with CoreAudio to identify and mute the audio stream from Spotify's process ID. This targeted approach was more elegant, preventing interruption of other audio sources like notifications or music production software.

The menu bar interface demonstrates clean SwiftUI principles for persistent background apps. Rather than a traditional window-based application, Spotifree lives entirely in the macOS menu bar using NSStatusBar. This keeps the app accessible without consuming dock space or requiring window management. Users interact through a simple dropdown menu showing current status and configuration options.

The evolution from AppleScript prototype to native Swift application reveals important architectural decisions. The original proof-of-concept was pure AppleScript, running as a background process. While functional, AppleScript's performance and reliability limitations made it unsuitable for continuous polling. The Swift rewrite improved response time, reduced CPU usage, and enabled proper error handling when Spotify wasn't running or became unresponsive.

Gotcha

Spotifree is officially discontinued, and for good reason: its core assumption is fundamentally brittle. The track number heuristic depends entirely on Spotify's internal implementation details, which carry no guarantees. Spotify could change this behavior in any update—deliberately to break ad-blocking tools or incidentally during refactoring. Indeed, reports from users in the project's final months suggest detection became increasingly unreliable.

The 300ms polling interval introduces a noticeable delay. Users hear the first fraction of a second of each ad before muting kicks in. While brief, this creates an unpleasant jarring effect, especially with loud advertisements. More aggressive polling would reduce this delay but increase CPU usage and battery drain on laptops. The project never solved this fundamental trade-off between responsiveness and resource consumption. Additionally, the muting approach doesn't actually block ads—Spotify still serves them, potentially affecting recommendation algorithms and artist metrics. Users seeking true ad-blocking need solutions that prevent ad delivery entirely, not just audio playback.

Verdict

Use if: You're learning macOS automation with Swift and want a well-structured example of scripting bridge integration, menu bar app development, and creative problem-solving with limited APIs. The codebase is clean, well-commented, and demonstrates real-world patterns for background macOS utilities. It's also useful for understanding why heuristic-based solutions eventually fail in adversarial environments. Skip if: You need actual ad-blocking for Spotify today—this app doesn't work with current versions and won't be updated. Just pay for Spotify Premium. The developer experience and artist support justify the cost far more than maintaining fragile workarounds. If Premium isn't an option, explore actively maintained alternatives, but understand that ad-blocking on native apps remains an arms race you'll likely lose.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/automation/artemgordinskiy-spotifree.svg)](https://starlog.is/api/badge-click/automation/artemgordinskiy-spotifree)