Skip to content

How NOOP Works

NoopApp edited this page Jun 10, 2026 · 1 revision

How NOOP Works

NOOP is a fully offline companion app for WHOOP straps (4.0 and 5.0/MG). It pairs directly with your strap over Bluetooth, decodes biometric data on your device, stores everything locally in SQLite, and computes recovery, strain, HRV, and sleep metrics without ever touching the internet. This page explains the big picture and how your data flows.

Not affiliated with WHOOP. NOOP is an independent, interoperability project built on community reverse-engineering. It is not a medical device — all metrics are approximate. Use only with a device you own. See Disclaimer for the full legal notice.


The One-Way Pipeline

NOOP is built around a simple flow: biometric data flows in from the strap (or from an import file), gets decoded into typed records, lands durably in a local SQLite database, is read back through a thin repository layer, is transformed into daily metrics by pure analytics functions, and finally renders in the UI. Nothing ever leaves your machine.

                          ┌──────────────────────────────────────────────┐
   WHOOP strap            │      NOOP (entirely on your device)         │
   (4.0 or 5.0)           │                                              │
                          │  BLE decode → SQLite → Analytics → UI        │
   ┌──────────────┐       │                                              │
   │ Bluetooth LE │───────┼──▶ Decode frame bytes                        │
   │ (your data)  │  BLE  │    ├─ Extract heart rate, HRV                │
   └──────────────┘       │    ├─ Extract sleep, SpO₂, temp, respiration │
                          │    └─ Extract battery & device events        │
                          │           │                                  │
                          │           ▼                                  │
   ──────────────────────┼─────► SQLite database                         │
   CSV export            │    (all data stored locally)                  │
   or Apple Health       │           │                                  │
                          │           ▼                                  │
                          │    Pure analytics math (on-device)           │
                          │    ├─ Sleep staging from biometrics         │
                          │    ├─ Recovery scoring vs your baseline     │
                          │    ├─ Strain calculation from HR            │
                          │    └─ HRV, RHR, zone analysis              │
                          │           │                                  │
                          │           ▼                                  │
                          │    SwiftUI screens (macOS)                   │
                          │    Jetpack Compose (Android)                │
                          │                                              │
                          └──────────────────────────────────────────────┘

All the work happens in five platform-agnostic Swift packages:

Package What it does
WhoopProtocol Decodes BLE frames from the strap into typed fields. Handles CRC checksums, reassembles fragmented packets, and understands both WHOOP 4.0 and 5.0 protocols.
WhoopStore Manages the SQLite database. Handles schema migrations, stores decoded streams (heart rate, RRV, SpO₂, temperature, etc.), and caches computed metrics.
StrandAnalytics Pure functions for physiology math. Computes HRV (Task Force 1996), recovery scores, strain (Karvonen %HRR + TRIMP), sleep staging, and correlation analysis.
StrandImport Imports your existing history. Parses WHOOP CSV exports and Apple Health exports so you can bring years of data into NOOP at once.
StrandDesign The visual design system — colors, typography, charts (sparklines, heatmaps, hypnograms), and SwiftUI components.

The macOS app target (Strand/) is the reference implementation. Android and iOS use the same five packages, so all core logic is truly cross-platform.


Two Independent Data Paths: Live + Historical

NOOP has two BLE routes, optimized for different purposes:

Live Path (Real-time, optional for UI)

Every ~1 second, your strap broadcasts real-time heart rate and R-R intervals to NOOP. This arrives on both:

  1. The standard BLE Heart Rate Measurement characteristic (0x2A37) — works even without bonding, carries a wall-clock timestamp.
  2. The custom REALTIME_DATA frames — proprietary format, flows only after bonding, used mainly for UI responsiveness and events (double-tap, wrist on/off).

The live path is the most responsive but the least complete — the strap's live stream carries sparse samples. NOOP records standard HR/RR continuously and independently of everything else.

Why it matters: you see live HR ~1 Hz on the Live screen, haptic-coaching is real-time, and automations (like locking your Mac when you remove the strap) are instant.

Historical Path (Periodic offload, the primary metric source)

Your strap stores ~14 days of rich biometric history: heart rate, R-R intervals, SpO₂, skin temperature, respiration, and accelerometer data. NOOP offloads this history the same way the official app does:

  1. On connect: the first sync happens automatically after ~1.5 seconds.
  2. Every 15 minutes (by default) while connected and bonded.
  3. On demand if you double-tap the strap.

The offload is a state machine:

send SEND_HISTORICAL_DATA
  │
  ▼
HISTORY_START (open the chunk)
  │
  ├─ HISTORICAL_DATA (type-47 record) … (repeat ~50 records per chunk)
  │
  ├─ HISTORY_END (close the chunk and give trim cursor)
  │    ├─ decode the chunk
  │    ├─ write decoded data to database ← DURABLE
  │    ├─ send ack with the trim cursor
  │    └─ strap now forgets that chunk (only after ack)
  │
  └─ HISTORY_COMPLETE (offload finished)

Why it's robust: data is written to your database before the strap is told to forget it. If the connection drops mid-offload, the next session resumes exactly where it left off — no data is ever lost, no re-syncing needed.

Why historical is primary: it carries real unix timestamps and rich biometric fields. The live stream is mostly HR/RR. Recovery, strain, and sleep scoring are computed from the historical data.


The Database: SQLite Local-First

Everything lives in a single SQLite file at:

macOS: ~/Library/Application Support/OpenWhoop/whoop.sqlite

The schema has four groups:

1. Device Registry

  • One row per strap you've bonded (device table).

2. Decoded Streams (the raw measurements)

  • hrSample — heart rate (BPM) at each timestamp.
  • rrInterval — R-R intervals (beat-to-beat milliseconds) for HRV analysis.
  • spo2Sample, skinTempSample, respSample, gravitySample — SpO₂, temperature, respiration, accelerometer from type-47 offload.
  • event — strap events (battery level, wrist on/off, double-tap, device alarms).
  • battery — battery state of charge and voltage.

All are durable — they are never deleted or pruned.

3. Metric Caches (the rolled-up results)

  • dailyMetric — one row per calendar day: recovery score, strain, sleep minutes (deep/REM/light), resting HR, average HRV, SpO₂%, temperature deviation, respiration rate, workout count.
  • sleepSession — one row per sleep session: start/end times, efficiency, resting HR, HRV, and a hypnogram (the sleep-stage diagram).
  • workout — detected exercise sessions with duration, energy, average/max HR, strain, and distance.
  • journal — user-answered daily prompts (mood, stress, etc.).
  • appleDaily — Apple Health daily aggregates (steps, calories, weight, VO₂max, etc.) if you imported them.
  • metricSeries — a generic long-format table for any scalar metric, so you can query and compare metrics uniformly.

4. Bookkeeping & Raw Outbox

  • cursors — watermarks for resumable offload (so the next connect knows where the last one left off).
  • rawBatch — optionally, the original BLE frame bytes in compressed form (only if you enable the research toggle). Pruned every 24 hours, but decoded streams are never touched.

Pairing: BLE Bond Once, Read Forever

WHOOP 4.0

  1. Scan for the strap's custom Bluetooth service.
  2. Connect and discover characteristics.
  3. NOOP sends a benign GET_BATTERY_LEVEL command over BLE with confirmation requested.
  4. The strap's confirmation creates a bond (one-time pairing).
  5. After bonding, NOOP sends a handshake: HELLO, SET_CLOCK, GET_CLOCK, then begins the first offload.

WHOOP 5.0 / MG

The 5.0 strap uses the same bonding concept but a different transport ("puffin" protocol). The pairing process is the same, but:

  • The strap uses CRC16-Modbus instead of CRC8 for header integrity.
  • There's a static CLIENT_HELLO frame the app sends.
  • Packet types are slightly different.

NOOP's protocol layer handles both transparently — the rest of the code doesn't care which generation you're using.

Note: The strap holds a single encrypted bond at a time. If it's bonded to the official WHOOP app on your phone, NOOP can't get the full bond (you'll get "Encryption is insufficient" errors). To pair NOOP, close the official app, put the strap in pairing mode, and try again. See Strap Support and Pairing for detailed steps.


How Data Becomes Metrics: The Analytics Pipeline

All metric math is pure functions — they never touch the database. Given a day's raw streams and your personal profile, they compute recovery, strain, sleep stages, and more.

Sleep Staging

Analyzes cardiorespiratory features (HRV, RR stability) and motion (gravity vector) to detect:

  • In-bed vs awake (binary).
  • Deep, REM, light, and awake stages (if you're wearing the strap at night).

Produces sleep efficiency, resting HR, average HRV, and a hypnogram showing when you transitioned between stages.

Recovery Score (0–100)

A composite metric based on:

  • Nightly HRV vs your personal baseline (if HRV dropped, recovery is lower).
  • Resting heart rate trend (if RHR crept up, recovery is lower).
  • Sleep quality (a proxy from efficiency, deep/REM ratio, and disturbances).

After a few nights of data, NOOP learns your baseline and the score becomes more personalized.

Strain (0–21 scale)

Integrates the day's heart rate into a cardiovascular load score:

  • Based on Karvonen %HRR (percentage of max HR you spend training).
  • Weighted by Edwards TRIMP (the longer at elevated HR, the higher the strain).
  • Capped at 21 to match WHOOP's scale.

Sedentary days score near 0; hard training days score higher.

HRV & Resting HR

  • HRV (RMSSD) — computed from R-R intervals using Task Force 1996 standard, filtered for ectopic beats.
  • Resting HR — the lowest HR during sleep.

Other Metrics

  • VO₂ max (if imported from Apple Health).
  • Heart-rate zones — custom (by age/max HR) or from your profile.
  • Correlations — the app can correlate any two metrics (e.g., "is my recovery lower on high-stress days?").

Import: Bring Your History In

Already have WHOOP data or Apple Health exports? NOOP can import them once, and they're merged into the same database as your live strap data.

WHOOP CSV Export

If you've downloaded your data from the official app as a .zip containing:

  • physiological_cycles.csv
  • sleeps.csv
  • workouts.csv
  • journal_entries.csv

NOOP parses each file and loads it into the database. The same import handles WHOOP 4, 5, and MG exports.

Apple Health Export

From the Health app on your phone, export your data as export.xml (can be >1 GB). NOOP streams the parsing (so it doesn't blow up memory) and imports:

  • Daily summaries: steps, active energy, basal energy, heart rate, VO₂max, weight.
  • Workouts with duration and energy.
  • Sleep records.

Both import paths converge on the same WhoopStore database, so your imported history lights up instantly on all screens — no waiting for live sync.


Cross-Platform Architecture: macOS → Android → iOS

NOOP's core logic lives in five Swift packages that declare both iOS 16+ and macOS 13+, with all UI-framework code behind #if canImport(UIKit) / #if canImport(AppKit) guards.

Platform Status Details
macOS ✅ Full Reference implementation. SwiftUI, all features.
Android ✅ Full Native Kotlin port. Jetpack Compose, all features. Grab the APK from Releases.
iOS 🧪 Experimental Community port in PR #42. Builds from source only (no App Store — Apple requires a real developer identity, incompatible with this project's anonymity).

All three platforms:

  • Pair over BLE with the same protocol logic.
  • Decode frames with the same WhoopProtocol package.
  • Store data in the same SQLite schema via WhoopStore.
  • Compute metrics with the same StrandAnalytics functions.

The only difference is the UI layer.


On-Device Behaviors: No Cloud, All Local

Beyond reading metrics, NOOP can automate things on your device — all running locally:

Haptic Coaching

  • HR-zone haptic feedback: buzz when you enter/leave a heart-rate zone during exercise.
  • HRV breathing guide (Breathe mode): pace your breathing with haptic cues and show live HR + HRV as you relax.

Automations (macOS)

  • Double-tap actions: lock your Mac, dismiss notifications, mark a moment, or run a Shortcut by name.
  • Wrist wear/remove: lock when you take off the strap; run a Shortcut when you put it back on.
  • Smart alarm: schedule a firmware alarm that fires even if the Mac is asleep.
  • Stress nudges: the strap buzzes if autonomic load is creeping up.

Intervals (Silent Timer)

  • HIIT timer: the strap buzzes every transition (work → rest, countdown ticks, finish), no screen needed.

All of these are optional, default-off, and compute entirely on-device using your live biometric stream.


Concurrency & Safety

NOOP uses a careful concurrency model to avoid data corruption:

  • WhoopStore is an actor — all database writes run serially on its own executor (off the main thread), so GRDB's blocking calls don't freeze the UI.
  • BLE, UI state, and orchestration run on the main thread — CoreBluetooth callbacks arrive there anyway.
  • The historical offload drains frames one-by-one — so HISTORY_START → records → HISTORY_END can never be reordered mid-chunk, and trim cursors are always safe.

SQLite settings:

  • WAL (write-ahead logging) — two simultaneous readers (UI + collector) never deadlock.
  • 5-second busy timeout — if write contention happens, readers wait rather than fail immediately.
  • 16 MB page cache — faster multi-thousand-row batch writes during offload and import.

Result: smooth UI, durable data, no crashes from concurrent access.


What Happens When the Connection Drops

NOOP is resumable by design:

  1. Live path: live HR/RR notifications stop, but re-connect simply resumes.
  2. Historical offload: the strap_trim cursor is durably stored. The next offload picks up where the last left off — no data is re-synced or lost.
  3. Import: runs to completion once; re-import is safe because the database dedupes by natural key.

Performance: What You Get vs. Latency

Feature Latency Notes
Live HR/RR ~1 sec real-time BLE notifications
Double-tap response ~1 sec instant
Recovery / strain / sleep ~1–2 min after offload computed from type-47 history
Import full year ~1–2 min streamed, doesn't block UI
Metric correlations ~few sec computed on-demand from database

The app is responsive; heavy operations (big imports, correlation analysis) run off the main thread.


Transparency & Trust

Every number NOOP computes comes from documented, published methods:

  • HRV: Task Force 1996 standard (RMSSD, SDNN).
  • Recovery: published HRV/RHR baselines + logistic composite (cited in-code).
  • Strain: Karvonen %HRR (age-based max HR) + Edwards TRIMP.
  • Sleep staging: cardiorespiratory + motion heuristics (approximate, not validated).

Crucially:

  • NOOP is not a medical device — all metrics are approximations for personal insight, not diagnosis.
  • The source code is open — inspect the StrandAnalytics package to see exactly how every number is calculated.
  • No proprietary black box — you're not trusting a neural network trained on unknown data; you're trusting documented math you can verify.

Related Pages

  • Strap Support and Pairing — detailed steps for pairing 4.0 and 5.0.
  • Protocol — the full BLE reverse-engineering reference (frame format, commands, offload state machine).
  • Privacy and Security — data privacy, optional AI Coach, and security considerations.
  • Features — full list of screens and automations.
  • Contributing — how to help with reverse-engineering, Android porting, or other work.

Clone this wiki locally