Swift

Your AirPods Know More About Your Posture Than You Think

AirPods have an accelerometer and a gyroscope streaming head-orientation data all day, and almost nobody reads it. I spent a weekend building a macOS app that does — and found a live breathing-rate monitor hiding in the same signal.

FN
Fahid Nadeem
May 15, 2026 · 5 min read

Our AirPods know more about us than we think. They have an accelerometer, a gyroscope, and they know exactly how your head is oriented in space — updated dozens of times per second, all day long.

Apple uses that data for spatial audio and ear detection. But the raw signal is sitting right there, accessible to any developer through Core Motion. Most people have no idea. I got curious about what else you could do with it, so I spent a weekend finding out.

What I Built

The app sits quietly in your macOS menu bar and uses your AirPods' motion sensors to monitor your posture in real time. Green when you're upright, red when you're not. Slouch for more than ten seconds and it notifies you.

Live posture score dropping the moment I lean forward.

No new hardware, no wearable — just sensors you're already wearing.

The API

The class is CMHeadphoneMotionManager, part of Core Motion. Apple added it in iOS 14 and macOS 14. You call startDeviceMotionUpdates and it streams CMDeviceMotion objects at around 50 Hz — attitude, gravity, rotation rate, user acceleration, the full suite you'd normally get from an iPhone.

One gotcha nobody warns you about: if you call startDeviceMotionUpdates once at launch and the AirPods aren't connected yet, the stream just never starts. No retry, no error — it silently does nothing. I had to implement CMHeadphoneMotionManagerDelegate to listen for the connect event and restart the stream every time headphones are detected.

How Posture Detection Works

I use CMDeviceMotion.attitude.pitch, which measures forward-and-backward head tilt in radians. The key constraint is you can't hardcode a baseline — everyone holds their head differently, and AirPods sit differently in every ear. So there's a calibration step: press Calibrate, and the app samples your head pitch continuously for two seconds and averages the readings. That average becomes your personal baseline for "upright," stored in UserDefaults so it persists across launches.

From there, every frame:

private func updateScore(pitch: Double) {
    guard let base = baselinePitch else { return }
    let deviation = abs(pitch - base)
    let maxDeviation = 0.35   // roughly 20 degrees
    postureScore = max(0, min(100, 100.0 * (1.0 - deviation / maxDeviation)))
    checkBadPosture()
}

0.35 radians is about 20 degrees — at that deviation the score hits zero. In practice most people get a warning well before that.

The Ten-Second Rule

Slouch detection doesn't fire the moment your score drops. It starts a timer the first time you cross below 50%. Correct yourself before ten seconds and the timer resets. Stay slouched for ten continuous seconds and it fires a notification — then won't send another for sixty seconds even if you're still slouched, so it can't spam you.

private func checkBadPosture() {
    if postureScore < 50 {
        if badPostureStart == nil {
            badPostureStart = Date()
        } else if let start = badPostureStart,
                  Date().timeIntervalSince(start) > 10 {
            sendNotificationIfNeeded()
        }
    } else {
        badPostureStart = nil
    }
}
 
private func sendNotificationIfNeeded() {
    let now = Date()
    if let last = lastNotificationTime,
       now.timeIntervalSince(last) < 60 { return }
    lastNotificationTime = now
    // send UNUserNotificationCenter request
}

The Bug That Cost Me Three Hours

My first version computed deviation as a full 3D angle between gravity vectors — dot product, acos, the works. It looked more correct on paper. In practice it was terrible, because the gravity vector changes when you turn your head left or right, not just when you lean forward. Glancing at a second monitor would tank the posture score even with a perfectly straight spine.

Going back to plain attitude.pitch fixed it immediately. Pitch only changes on the axis that matters for posture — forward and backward tilt. Turning your head doesn't touch it.

What Surprised Me

While staring at the raw motion data sitting completely still, I noticed a tiny rhythmic oscillation in the signal. Not noise — breathing. Every inhale and exhale moves your head by a fraction of a millimeter, invisible to the eye, completely visible to the accelerometer.

Add a stillness gate, a detrend pass, and peak detection, and you have a live breathing-rate monitor falling out of the exact same data stream. That feature took three hours.

The Bigger Point

We're walking around with sophisticated sensor arrays in our ears, on our wrists, in our pockets. Most apps treat them as input devices — tap here, swipe there. But the passive data these sensors produce continuously is mostly untouched: head orientation, micro-movements, breathing patterns, gait. All of it is there, all of it is accessible, almost none of it is being used in interesting ways.

The next wave of health and wellness software might not need new hardware at all. It just needs developers willing to look at what the sensors already on us are telling us.