LIAO, DI ACHILLE, WU, BORAC ET AL. (GOOGLE RESEARCH) · NATURE 2026

Passive Heart-Rate Monitoring

Every time you unlock your phone, the front camera can read your pulse off your face — no wearable, no effort. The hard part isn't the signal. It's making it work equally well for every skin tone.

Prerequisites: a sine wave is a frequency + "a neural net outputs probabilities." We build photoplethysmography, FFTs, entropy, and the Kalman filter from zero.
10
Chapters
9
Interactives
355k
videos validated

Chapter 0: A Vital Sign Hiding in Plain Sight

Your resting heart rate (RHR) — how fast your heart beats when you're calm — is one of the most quietly powerful numbers in medicine. A rising RHR over months is associated with higher mortality and cardiovascular events. It's a genuine biomarker of how your heart is doing. The catch: to track it longitudinally, you've basically needed a wearable strapped to your wrist or chest, every day, for months. Most people who'd benefit don't have one.

Meanwhile, there's a sensor almost everyone already carries and stares into dozens of times a day: the front-facing camera of a smartphone. Phones are owned by 90% of US adults, unlocked on the order of 144 times a day. What if, every time you glanced at your phone, it could read your heart rate off your face — passively, in the background, no effort at all? Track that across the day and you could estimate a daily RHR without anyone ever putting on a device.

This is exactly what the paper builds: PHRM — Passive Heart-Rate Monitoring. A deep-learning system that, on each screen-unlock, grabs an 8-second clip of your face and infers your heart rate from tiny, invisible color changes in your skin. Aggregate the day's measurements and you get a daily RHR. The technique — reading a pulse from ordinary video — is called remote photoplethysmography, or rPPG, and we'll demystify it in Chapter 1.

But here's the catch that makes this a hard and important paper, not just a clever demo. rPPG works by watching light reflect off blood under the skin — and melanin in darker skin absorbs more of that light, weakening the signal. Historically, rPPG (and even hospital pulse oximeters) have been significantly less accurate for darker skin tones. A health tool that works well for some skin tones and poorly for others isn't just technically incomplete — it's inequitable, and bodies like the FDA now demand diversity in validation. The central achievement of PHRM is accuracy that holds across skin tones.

The stakes, stated plainly. This isn't only "can a phone measure heart rate?" — rPPG demos have existed for years in controlled labs. It's "can a phone measure heart rate passively, in the chaos of real life, equally well for everyone, at a scale large enough to prove it?" The answer required a careful pipeline, a clever way to handle uncertainty, and the largest validation study of its kind: 355k videos across ~700 participants.
The Opportunity — passive vs. deliberate measurement

A wearable measures continuously but few people own one. A clinic RHR is one rare snapshot. PHRM catches a quick reading on each unlock — sparse per measurement, but dense over a day. Drag the unlocks-per-day slider and watch how many heart-rate samples accumulate, and how the daily estimate's uncertainty shrinks.

Phone unlocks per day 60
What is the central hard problem PHRM had to solve, beyond just measuring HR from video?

Chapter 1: Light, Blood, and the Pulse You Can't See

How can a camera possibly see your heartbeat? The answer is photoplethysmography (PPG) — a mouthful that just means "measuring blood volume with light." Each time your heart beats, it pushes a pulse of blood through the vessels just under your skin. More blood means slightly more light absorbed and slightly less reflected back. So with every heartbeat, your skin gets imperceptibly darker, then lighter, then darker again — a rhythmic flicker far too subtle for the eye, but not for a camera averaging over many pixels.

Remote PPG (rPPG) does this at a distance, from ordinary video: track a patch of facial skin across frames, average its color, and you recover a faint waveform that rises and falls with the pulse. The frequency of that waveform is the heart rate. A pulse oximeter on your fingertip does the same thing with a dedicated LED; rPPG does it with the front camera and the ambient light already in the room.

Now the crux of this whole paper. The pulsatile signal is tiny — a fraction of a percent change in brightness — sitting atop a mountain of noise: lighting shifts, head motion, camera sensor noise. The ratio of the real signal to that noise is the signal-to-noise ratio (SNR), and everything downstream depends on it. And here's the equity problem made physical: melanin, the pigment that makes skin darker, absorbs light. More melanin means less of the light ever reaches the blood and bounces back carrying pulse information — so the pulsatile signal is inherently weaker for darker skin, and the SNR is lower. Same heartbeat, fainter trace. That single fact is why naive rPPG methods degrade for darker skin tones, and why making PHRM equitable was the hard part.

The Pulse Signal vs. the Noise Floor

The teal wave is the true pulse-driven brightness change in a skin patch. The jagged warm line is what the camera actually records — signal plus noise. Raise melanin (darker skin) and watch the pulse amplitude shrink toward the noise floor; raise motion/lighting noise and watch it drown. The recoverable heart rate lives or dies by this ratio.

Melanin (skin pigmentation)light
Motion + lighting noisemoderate
Misconception — "darker skin has a different heartbeat signal." The heartbeat is identical; the optical signal reaching the camera is weaker because melanin absorbs light before it can probe the blood. It's a sensing problem, not a physiology problem. That's important: it means the fix is better signal extraction and honest uncertainty handling — not different medicine. PHRM's job is to pull a reliable rate out of a fainter trace, and to know when it can't.
Why does rPPG historically perform worse on darker skin tones?

Chapter 2: The Pipeline — From Unlock to RHR

Before diving into any single component, let's see the whole machine. PHRM has two modules that chain together: a per-video HR module that turns one 8-second clip into a heart-rate measurement (with a confidence score), and a per-day RHR module that aggregates a day's worth of those measurements into a single resting heart rate.

It all begins with a trigger you perform constantly without thinking: a screen-unlock. That event tells PHRM to quietly capture an 8-second front-camera clip. From there, each clip flows through preprocessing, a neural network, and a confidence gate — and only the clips that survive the gate feed the daily aggregation and a Kalman filter that produces the final RHR.

The PHRM System (Figure 1a, interactive)

Click any stage to see what data flows through it — shapes, the engineering choice, and what happens when it fails. The teal stages are the per-video HR module; the warm stages are the per-day RHR module. Notice the gate in the middle: most real-world clips never make it past it, and that's by design.

Two design decisions are worth flagging now, because they recur. First, PHRM throws away most of its inputs — in free-living use only about 43% of clips yield a valid measurement. That sounds wasteful until you remember the trigger is free and constant: better to discard a noisy clip than to report a wrong heart rate. Second, the whole thing is built to run on-device, on a phone, which forces every component to be computationally cheap — the reason the network is tiny and the video gets crushed to 32×32 pixels (Chapter 3).

Concept → realization, at a glance. Input: an 8-s clip (≈120 usable frames). HR module output: one HR value in 40–180 bpm plus a confidence. Gate: keep or discard. RHR module input: all of today's kept HRs. Output: one daily RHR. The cleverness is concentrated in three places — how the network expresses uncertainty (Ch. 4), how the gate uses it (Ch. 5), and how the day's noisy estimates become one stable number (Ch. 6–7).
In free-living use, only ~43% of captured clips produce a valid HR measurement. Why is discarding the rest the right design?

Chapter 3: Preprocessing — Squeezing Out the Pulse

Raw phone video is a terrible input: shaky, different frame rates per device, mostly irrelevant background, and far too high-resolution to run a network on every unlock. So PHRM applies a five-step preprocessing pipeline that turns a messy clip into a clean, tiny tensor that foregrounds the pulse.

1 · Stabilize
Affine-warp each frame to align facial-landmark centroids — cancels head motion so the skin patch stays put. Motion is the #1 noise source.
2 · Standardize to 15 fps
Phones record at different frame rates; linear interpolation resamples every clip to a consistent 15 Hz → 120 frames for 8 s.
3 · Crop the face
Tight bounding box (20% margin) isolates skin and drops the background — less noise, less compute.
4 · Resize to 32×32
Anti-aliased downsample to a tiny 32×32 grid. Spatial detail doesn't carry the pulse — the average color does — so this is nearly free signal-wise and huge for on-device speed.
5 · Frame-difference
Subtract consecutive frames. The static face cancels out; what remains is change — and the rhythmic pulsatile change is exactly what we're after.

Step 5 is the quiet hero, so let's dwell on it. The face barely changes frame to frame — the pulse is a fraction of a percent of brightness. If you feed raw frames to a network, that tiny signal is buried under the huge constant of "this is a face." But subtract consecutive frames and the constant vanishes, leaving only what moved: a little motion, a little noise, and — rhythmically — the pulse. Frame-differencing is a cheap high-pass filter that throws away the boring constant and keeps the interesting change.

Frame Differencing Reveals the Pulse

Left: the average brightness of the skin patch over time — the pulse is a barely-visible ripple on a big flat baseline. Right: after frame-differencing, the flat baseline is gone and the rhythmic pulse pops out. Add motion to see the trade-off: differencing also amplifies motion artifacts, which is why stabilization comes first.

Head motion (artifacts)low
Why 32×32 doesn't throw away the signal. It feels reckless to crush a face to 32×32 pixels. But the pulse lives in the temporal average color of skin, not in spatial detail like wrinkles or eyelashes. Averaging many pixels into a few actually improves SNR (random pixel noise cancels), while slashing compute so the model runs on a phone. The resolution you'd want for face recognition is the opposite of what you want for pulse extraction.
What does the frame-differencing step accomplish?

Chapter 4: Heart Rate as a Classification Problem

Now the core network, and the single most important modeling decision in the paper. The preprocessed 120-frame clip goes into a TSCNN — a temporal-shift convolutional network, chosen because it models time dependencies cheaply enough to run on a phone. Its first job is to output a pseudo-PPG: a clean, denoised waveform representing the pulse it extracted from the messy frame-differenced input.

Here's the elegant part. A heart rate is just the frequency of that waveform — so the network applies a Fast Fourier Transform (FFT), converting the time-domain pseudo-PPG into a frequency spectrum. A clean pulse shows up as a sharp peak at one frequency; that frequency, times 60, is the beats per minute. The FFT is the bridge from "wiggle over time" to "which heart rate."

But the network doesn't just read off the peak. It buckets the frequencies into 1-bpm bins across the biologically plausible range of 40–180 bpm, applies a softmax, and produces a full probability distribution over heart rates. The final HR is the weighted sum (the distribution's center of mass). PHRM thus reframes a regression problem ("predict the number 72") as a classification problem ("assign probability to each of the 141 possible bpm values"), trained with focal loss on the bucketed ground truth.

Why on earth turn a number-prediction into a 141-way classification? Because a distribution can express uncertainty, and a single number cannot. When the input is clean, the distribution is a sharp spike — "I'm confident it's 72." When the input is garbage (extreme motion, no face), the distribution goes flat — "I have no idea." A regression model, forced to emit one number, would confidently output a wrong 95 and you'd never know it was guessing. The flat-vs-peaked shape of this distribution is the foundation of everything in the next chapter.

Pseudo-PPG → FFT → HR Distribution (SHOWCASE)

Set the true heart rate and the input noise. Top: the pseudo-PPG waveform the network extracts. Middle: its FFT spectrum. Bottom: the softmax distribution over 40–180 bpm, with the weighted-sum HR marked. Crank the noise and watch the sharp peak collapse into a flat, uncertain distribution — the model honestly signaling "I don't know," which a single-number regressor never could.

True heart rate72 bpm
Input noise (motion / low SNR)low
# PHRM-HR head: regression reframed as classification (PyTorch-style)
def hr_head(frames):                       # frames: (120, 32, 32, 3) frame-differenced
    pseudo_ppg = tscnn(frames)                # (120,) extracted pulse waveform
    spectrum   = torch.fft.rfft(pseudo_ppg).abs()       # time -> frequency
    # keep only 40-180 bpm, one bin per bpm (141 classes)
    bins   = bucketize(spectrum, lo=40, hi=180, width=1)   # (141,)
    probs  = torch.softmax(bins, dim=-1)         # distribution over HR
    hr     = (probs * torch.arange(40, 181)).sum()   # weighted-sum HR
    return hr, probs                          # probs shape = the uncertainty signal

# trained with focal loss on the bucketed ground-truth bpm — NOT MSE on a scalar
loss = focal_loss(probs, one_hot(round(true_hr) - 40))
The distribution IS the product. Most of the time we talk about a model's prediction as "the answer." Here, the shape of the answer is just as valuable as its center. Peaked = trust me; flat = don't. By choosing classification + softmax over regression, the authors got an uncertainty estimate for free, baked into the output. The whole real-world robustness of PHRM — surviving the chaos of free-living use — rides on this one choice. (Final predictions also ensemble the top five models for stability.)
Why does PHRM frame HR estimation as classification over 1-bpm bins rather than regressing a single number?

Chapter 5: Confidence Gating — Knowing When to Stay Quiet

We ended Chapter 4 with a distribution that's peaked when the model is sure and flat when it isn't. The confidence gate turns that shape into a yes/no decision: is this measurement trustworthy enough to keep, or should we silently discard it? In the wild — phones at weird angles, faces half-covered, people walking — most clips deserve to be discarded, and the gate is what makes "report nothing" better than "report garbage."

How do you measure "peakiness" with a single number? With entropy. Entropy quantifies how spread-out a probability distribution is: a sharp spike has low entropy (all the probability mass is in one place — little disorder), while a flat distribution has high entropy (mass spread everywhere — maximum disorder). PHRM uses the negative entropy as its confidence score — flipping the sign so that higher = more confident. A peaked, certain distribution scores high negative entropy; a flat, clueless one scores low.

confidence = −H(p) = ∑k pk log pk

Here pk is the probability the model assigned to the k-th bpm bin, and the sum runs over all 141 bins. When one bin holds almost all the probability, this number is near zero (high confidence); when probability is smeared across many bins, it's a large negative number (low confidence). Compare it to a learned cut-off threshold: above, keep the measurement; below, discard. (The paper notes negative entropy beat alternatives like the pseudo-PPG SNR or the single max-probability — because it uses the whole distribution's shape, not one point of it.)

And here's where equity gets engineered into a threshold, not just hoped for. The cut-off wasn't chosen to maximize overall accuracy — it was searched under two hard fairness constraints: every skin-tone group's error (MAPE) had to be below 8%, and the gap between any two groups had to be below 3%. A more aggressive gate could have boosted the average while quietly failing dark-skin users; these constraints forbade that. Fairness became a literal inequality the threshold search had to satisfy.

The Gate — entropy decides keep or discard

As you add noise, the HR distribution (top) flattens, its entropy rises, and its negative entropy (confidence, the gauge) falls. Cross below the cut-off and the measurement is discarded — PHRM stays silent rather than report a guess. Drag the cut-off to feel the trade-off: stricter keeps only the surest clips but accepts fewer.

Input noiselow
Gate cut-off (strictness)moderate
Why the gate explains a paradox. PHRM's error was actually lower after exercise (2.74%) than at rest (6.01%) — backwards, since exercise means motion. The gate is why: post-exercise clips are noisier, so more get discarded (success rate 62% vs 78% at rest), and the ones that survive are the cleanest. The gate trades coverage for accuracy — and because the trigger is free, low coverage is fine. This is the same robustness principle as Chapter 2, now quantified.
PHRM uses the negative entropy of the HR probability distribution as its confidence score. What does a low (large-negative) value indicate?

Chapter 6: From Scattered HRs to a Daily Resting Rate

By the end of the day, PHRM has a scatter of valid heart-rate measurements — one per surviving unlock, taken at random moments: while you were calm, stressed, climbing stairs, sipping coffee. The RHR module's job is to distill this messy cloud into a single number that means "your heart rate at rest." The insight is beautifully simple and statistical, not neural.

Think about what those measurements look like as a distribution. Most of the day you're at or near rest, so the bulk of measurements cluster low. The high readings — the stairs, the stress — are the upper tail. Resting heart rate, by definition, is down at the low end of this distribution. So PHRM estimates it by taking roughly the tenth percentile of the day's valid HRs (plus a small constant bias-correction tuned once for everyone), then requires at least 20 valid measurements in a day before it'll commit to an RHR at all.

Why the tenth percentile and not, say, the minimum or the mean? The mean gets dragged up by every stressful moment — it measures your average day, not your resting state. The minimum is the noisiest single point, easily a fluke low reading. The tenth percentile threads the needle: low enough to capture rest, robust enough to ignore both a freak minimum and the entire upper tail. This is the quiet genius of passive sensing — you don't need any single measurement to be taken "at rest." Gather enough measurements and the resting state simply appears as the low percentile of the distribution. Arousal spikes can't move it because they're upper-tail outliers, filtered out by construction.

A Day of Heart Rates → the Tenth Percentile

Each dot is one valid measurement across a day. The teal line is the 10th-percentile RHR estimate; the dashed line is the (easily-fooled) mean. Hit "add arousal spikes" to simulate a stressful afternoon — watch the mean lurch upward while the 10th-percentile RHR barely flinches. That robustness is why passive sensing works without ever asking you to rest.

Valid measurements today80
The statistical superpower. A wearable computes RHR by detecting "at rest" moments with an accelerometer and averaging them. PHRM can't tell if you're resting — so instead it leans on volume: capture enough measurements and rest reveals itself as the low tail. Density beats deliberate measurement. The minimum-20 rule just guards against drawing a percentile from too few points to be meaningful.
Why does PHRM use the ~10th percentile of the day's heart rates (not the mean or the minimum) to estimate resting heart rate?

Chapter 7: The Kalman Filter — Smoothing Across Days

Each day now yields one RHR estimate — but each is itself noisy, drawn from however many measurements happened to survive that day. A day with 200 valid clips gives a tight estimate; a day with 22 gives a rough one. Plot the daily estimates raw and they jitter around the true underlying trend. PHRM's final touch smooths them with a Kalman filter.

The Kalman filter is the classic tool of optimal state estimation: how to track a quantity that changes slowly over time when each of your measurements is noisy. Its logic is a running negotiation. Each day it holds a prediction of your true RHR (essentially "probably close to yesterday's, since RHR drifts slowly"). Then today's noisy measurement arrives, and the filter blends the two — trusting the new measurement more when it's reliable (a day with many clips), trusting yesterday's accumulated estimate more when today is sparse. The output is a refined RHR that's more stable than any single day and tracks the real physiological trend.

The intuition for why this helps: your true resting heart rate doesn't teleport day to day — it drifts. So yesterday's well-supported estimate is genuine evidence about today. The Kalman filter is the principled way to combine "what we believed yesterday" with "what we measured today," weighted by how much we trust each. (We derive this trust-weighting — the Kalman gain — from scratch in the dedicated Kalman Filter lesson.)

Daily Estimates → Kalman-Smoothed Trend

Warm dots are raw daily RHR estimates (jittery); the teal line is the Kalman-filtered trajectory; the faint grey line is the true underlying RHR. Raise the per-day measurement noise and watch the raw dots scatter wildly while the filtered line stays calm and on-trend. Note the filter takes a few days to "warm up" and converge.

Per-day measurement noisemoderate
The full RHR chain. day's valid HRs → 10th percentile + bias (today's raw RHR, robust to arousal) → Kalman filter (blend with the running estimate, robust to day-to-day noise) → reported daily RHR. Two layers of statistics — one across measurements within a day, one across days — turn a stream of noisy 8-second guesses into a clinically meaningful trend. The result: daily RHR within ~4 bpm of a wearable reference.
What does the Kalman filter add on top of the per-day 10th-percentile RHR estimate?

Chapter 8: The Results — Accurate, and Equitable

A system like this is only as good as its validation, and this is where the paper earns its place. PHRM was developed on 192,353 videos from 485 participants and validated on 162,546 videos from 211 participants — the largest validation of its kind — spanning controlled lab studies and messy free-living use on participants' own phones (26 different models).

The headline accuracy numbers all clear the bar. Against reference ECG, free-living HR hit a MAPE of 6.09% at the participant level (lab: 5.65%), comfortably under the 10% industry standard (ANSI/CTA-2065). Daily RHR landed within a MAE of ~4.4 bpm of a wearable reference, under the prespecified 5 bpm target, and correlated with the wearable RHR at r = 0.87.

But the number that matters is the equity result. Across the three skin-tone groups, free-living HR MAPE was 5.04% (light), 5.12% (medium), 7.84% (dark) — all under 10%, and the differences between groups met a prespecified non-inferiority target (gaps below 5 percentage points). Of 15 state-of-the-art rPPG models tested on the same data, PHRM was the only one to stay under 10% for every skin tone. That's the whole point of the paper, in one comparison.

Skin-Tone Equity: PHRM vs. Prior rPPG (Figure 3)

Error (MAPE) by skin-tone group; the red line is the 10% accuracy standard. Toggle between PHRM and a representative prior rPPG model. Notice PHRM stays under the line for all three groups with a small gap, while prior methods blow past it — and worst for the darkest skin tones. Lower is better.

Finally, a validity check that the RHR is biologically real, not just internally consistent. In a statistical model, PHRM-derived RHR was independently predicted by two well-known cardiovascular risk factors: higher BMI raised RHR (+1.92 bpm per standard deviation) and better cardiovascular fitness lowered it (−1.90 bpm per SD of VO₂max). The passive smartphone signal recovers the same physiology a clinic would — which is the ultimate evidence that it's measuring something true.

What the paper is careful about — and you should be too. Success rate still drops with darker skin (free-living valid-measurement rate: 58% light vs 25% dark) — the accuracy when it fires is equitable, but darker skin yields fewer firing clips, so coverage is less equal. PHRM also needs a reasonably visible, reasonably still face, and it's a research system, not a cleared medical device. Equity in accuracy is a major step; equity in coverage remains open work.
What was the key equity finding that distinguished PHRM from 15 prior rPPG models?

Chapter 9: Connections & Cheat Sheet

PHRM is a masterclass in turning a faint, biased physical signal into a robust, equitable health measurement — not with one breakthrough, but with a chain of careful choices, each handling a specific failure mode. Let's lock it in.

The pipeline, end to end

# PHRM: one screen-unlock to a daily resting heart rate
# --- per-video HR module ---
clip   = capture_8s_video()                 # on screen-unlock, front camera
frames = stabilize(clip)                     # affine-warp to facial landmarks
frames = resample_15fps(frames)              # -> 120 frames
frames = crop_face(frames, margin=0.2)        # isolate skin
frames = resize(frames, 32, 32)              # tiny + on-device
frames = frame_difference(frames)            # cancel static face -> keep the pulse

hr, probs = tscnn_fft_head(frames)           # pseudo-PPG -> FFT -> softmax over 40-180 bpm
conf      = -entropy(probs)                   # peaked = confident
if conf > cutoff:  day_hrs.append(hr)        # GATE: discard noisy clips (~57% in the wild)

# --- per-day RHR module (needs >= 20 valid HRs) ---
raw_rhr   = percentile(day_hrs, 10) + bias    # rest = low tail, robust to arousal spikes
daily_rhr = kalman_update(prev_state, raw_rhr) # blend across days, smooth noise

Cheat sheet — every design choice and the failure it fixes

ChoiceWhat it fixes
Frame-differencingPulse buried under the huge static "face" baseline
32×32 resizeOn-device compute; averaging boosts SNR (pulse is in avg color)
FFT in the networkHeart rate is a frequency — read it where it's obvious
Classification + softmaxExpresses uncertainty; flattens when unsure (regression can't)
Focal lossTrains the bucketed distribution; handles class imbalance
Negative-entropy gatingDiscards untrustworthy clips using the whole distribution shape
Equity-constrained cut-offForces per-skin-tone MAPE <8% and inter-group gap <3%
10th-percentile RHRRest = low tail; robust to arousal spikes & freak minimums
Kalman filterSmooths noisy day-to-day estimates; tracks the slow trend

The key numbers

MetricValue
Free-living HR error (MAPE)6.09% participant-level (< 10% target)
HR MAPE by skin tone5.04% / 5.12% / 7.84% (light/med/dark) — all < 10%
Daily RHR error (MAE)~4.4 bpm (< 5 bpm target); r = 0.87 vs wearable
Validation scale162,546 videos / 211 participants
vs prior rPPGonly model of 15 under 10% for all skin tones

Where this sits in the field

Related lessons on Engineermaxxing

The takeaway. No single trick made PHRM work. A faint signal got foregrounded (frame-diff), read where it's clearest (FFT), expressed with its uncertainty (classification), filtered when untrustworthy (entropy gate), distilled robustly (10th percentile), and smoothed over time (Kalman) — with equity wired into the threshold itself. The lesson for any sensing system: respect the noise, quantify your uncertainty, and make fairness a constraint you optimize under, not a number you check at the end.

Press Teach Mode and explain why HR estimation is framed as classification — out loud, from memory. If you can connect the flat distribution to the confidence gate to the equity constraint, you own this system.