ppg-js docs

Signal quality in ppg-js

Learn the ppg-js quality contract, stable reason codes, signal indices, and withheld-reading behavior.

Signal quality contract

quality.good is true only when every check below passes. Checks run in this order. reason and code identify the first failure.

OrderCheckFailure code
1fingerState === 'MEASURING'no_finger, settling
2Clipped-pixel fraction is 5% or lesssaturated
3Motion is below the threshold when a source is attachedmotion
4Pulsatile AC/DC is at least 0.2%weak_pulse
5No more than 20% of candidate intervals are rejected over 60 secondsirregular
6At least 8 accepted intervals exist over 60 secondscollecting
7Median template correlation is at least 0.6morphology
8Interval and spectral heart rate agree within 25%double_count, missed_beats, fft_disagree

heartRate, rmssd, and sdnn are 0 whenever quality.good is false. heartRateRaw remains populated.

Withheld readings

ppg.addEventListener('metrics', ({ detail: metrics }) => {
  if (!metrics.quality.good) {
    console.log(metrics.quality.code, metrics.quality.reason);
    return;
  }
  renderReading(metrics);
});

The contract prevents a UI from accidentally presenting an untrusted value as a measurement.

Signal quality indices

Every window includes metrics.sqi:

FieldDefinition
score0 to 1 geometric mean of bounded sub-scores
skewness, kurtosisShape statistics of the bandpassed pulse
perfusionPulsatile amplitude divided by DC, as a percentage
relativePower, snrDbCardiac-band power over total power
zeroCrossingRateZero crossings per second of the bandpassed pulse
templateCorrelationMedian beat correlation with the mean beat
detectorAgreementAgreement between interval and spectral heart rates
artifactRatio, clippedFraction, motionQuality-gate inputs

Each beat and tachogram point also carries sqi, its template correlation. The composite score supports ranking and display. The accept or reject decision remains quality.good.