Body Signals

A personal project · built for one person · used every day

Your normal,
not the average.

Body Signals learns how your body usually behaves and tells you, in plain words, when it shifts. Every model has to pass a test before it can say anything. When the data can't support an answer, it says “I don't know yet”.

I built it for myself, not as a product: it reads my own Apple Watch and I use it every day — a short briefing on Telegram each morning, quick logs during the day, and a private dashboard when I want the full story.

  • 1 person, 1 watch, 1 database
  • 8 personal models
  • every one validated on synthetic people
synthetic Sleeping heart rate of a simulated person. Teal: their normal. Coral: the nights that leave it.

01 · The problem

Wearable apps are built for athletes.

Scores without receipts

Readiness, strain, body battery: single numbers with unpublished formulas. You can't check them, and they don't tell you how sure they are.

Population thresholds

A heart rate that's normal for most people can be high for you. Population ranges are wide, so they miss the changes that matter for you.

Training first

They answer “should I train hard today?”. Most people want to know why they feel off this week, and what in their routine actually changes it.

02 · The approach

Three rules the whole system follows.

  1. I

    Compared with you, not with everyone

    Every signal is judged against your own rolling baseline: medians and robust spread, weekday habits, and seasonal drift once there's a year of data. Nothing is graded against a population chart.

  2. II

    Honest statistics, or silence

    Each model is tested on simulated people where the truth is known, and on simulated people with no effect at all. If a model can't beat a simple baseline, it isn't shown. Uncertainty is always shown as a range.

  3. III

    Associations aren't causes

    Patterns are called associations, with false-discovery control. To find out if a habit really helps, the system runs a randomized N-of-1 experiment: your days are randomly split, and you are your own control group.

03 · See it

The same nights, judged two ways.

A simulated person, generated in your browser. Twice we planted something real: a week of the body fighting something off, then a lasting shift up. Switch between a population range and their own normal and see which one notices.

—nights outside the range
—of the 6 “fighting something” nights caught
—streaks flagged (2+ nights in a row)
sleeping heart rate their normal outside the range streak flagged
Show the data as a table
Weekly summary of the synthetic person

The illustrative population range is 45–75 bpm. The personal band uses the previous 28 nights (median ± 2 robust SD). The streak detector is a one-sided CUSUM, the same kind of detector studied for early warning on smartwatches.

04 · Regularity

How regular you sleep, in one number.

The Sleep Regularity Index compares every minute of your day with the same minute 24 hours earlier. In a large cohort study, sleep regularity was a stronger predictor of mortality than sleep duration. Drag the sliders and watch four weeks of sleep change.

—Sleep Regularity Index (−100 to 100)
—per day out of sync with the day before
—social jetlag (weekend mid-sleep shift)

05 · Architecture

From wrist to a sentence you can act on.

  1. SensorApple Watchheart rate, HRV, breathing, wrist temperature, SpO₂, sleep stages, activity
  2. BridgeHealth Auto ExportiOS app sends HealthKit data as JSON over HTTPS, in batches
  3. APIIngest on Fly.iotoken auth, strict validation, plausibility ranges, idempotent writes to SQLite
  4. FeaturesDaily featuressleep timing and stages, overnight vitals, waking resting heart rate, activity, quick logs
  5. ModelsEnginebaselines, hypothesis bank with FDR, latent state, phases, body clock, forecast, experiments
  6. DeliveryTelegram + dashboarda short morning briefing, a weekly report, and a private dashboard with “what it means / what to do”

06 · The engine

Eight small models, each with a rule for staying quiet.

Load × recovery

A Kalman-filtered latent state that combines seven overnight signals into one 0–100 number, with an 80% range.

Stays quiet unless the shared factor predicts new days better than any single signal.

Phase changes

Bayesian online changepoint detection proposes dates. A test that accounts for autocorrelation confirms them.

Stays quiet unless 2+ signals move together. One bad week is not a phase.

Body clock

Cosinor fit on minute-level heart rate: when the body rests, when it peaks, and how bedtime moves it.

Stays quiet about energy windows when the daily curve is flat.

Tomorrow

Forecasts tomorrow's overnight values, scored day by day without peeking against “tomorrow = today”.

Stays quiet until it beats the naive baselines by a margin.

N-of-1 experiments

14 days in randomized, balanced blocks. The analysis is Bayesian with a sceptical prior, and adherence takes one tap.

Stays quiet until the end. No peeking at interim effects.

Sleep regularity

Sleep Regularity Index and social jetlag, with block-bootstrap intervals.

Stays quiet about change unless the intervals of the two windows don't overlap.

Weekly HRV

The 7-night average of ln(HRV) compared with a band built from the 60 nights before it.

Stays quiet unless it's low for 5 days in a row, by a margin that matters.

Streaks

A one-sided CUSUM catches several nights in a row with the heart above your normal.

Never an alarm and never a diagnosis. One night alone can't trigger it.

07 · Validation

Tested on simulated people first.

The numbers below come from synthetic people with known ground truth: planted habit effects, illness episodes and level shifts, plus negative-control worlds where nothing happens. All runs are seeded and reproducible. These are not results on anyone's real data.

0alerts in negative-control worlds with no illness and no effects (240 days each)
16/16planted illness episodes flagged, 0–1 day before the person would log them
0.84 vs 0.71load index vs sleeping heart rate alone: correlation with the true state on days the model never saw
≤ 0.4false phase changes per 100 days per signal, even with strong autocorrelation
2%false “it helped” verdicts in N-of-1 experiments with no real effect
7 → < 1false HRV warnings per 100 days: a one-night rule vs the weekly view
78–87%directional predictions correct, against 50% chance
7 / 9planted habit effects found by about day 170. None of the null exposures was confirmed.

What didn't work is documented too: pure BOCPD gave up to 1.9 false alarms per 100 days, and a bootstrap that ignored neighbouring nights made intervals too narrow. Both were replaced and the numbers re-measured on fresh seeds.

08 · Privacy

Health data stays in one place.

  • Single user, single database. Raw samples never leave the server the owner controls.
  • Only aggregates go out. The Telegram messages go to the owner's own chat and carry daily summaries.
  • Optional LLM, kept on a leash. It only rewords facts the code already computed. A number guard rejects any figure it invents.
  • Encrypted backups. AES-256-GCM with a scrypt-derived key, and the restore is tested.
  • LGPD by design. Health data is sensitive personal data (Art. 11), so it's collected for one purpose, kept minimal and controlled by the owner.
  • Hardened service. Constant-time token checks, rate limits, strict CSP, a non-root container and hash-locked dependencies.
  • No third parties. No trackers, CDNs or external fonts, on the dashboard and on this page.
  • Not a medical device. It describes trends against your own normal. It never names a disease.

09 · Built with

Small, boring, cheap.