Skip to content
A conceptual visualization of stress physiology showing the interconnected nervous system response that wearable devices attempt to measure

What Goes Into Pulsyn's Stress Score

James Hoffmann James Hoffmann
June 4, 2026 · 13 min read

TL;DR

Most wearables compute a stress score by running your heart rate through a black box and giving you a number between 0 and 100. Pulsyn does the same thing, but the weights are public, the math is in the repository, and the app tells you exactly how confident it is about the result. The score is 35% ring stress, 25% HRV deviation, 20% heart rate elevation, 12% sleep quality, 5% temperature, and 3% SpO2. If you are missing data, the algorithm rebalances instead of guessing. That is the difference.

The stress score nobody explains

I checked the Oura app, the Whoop app, and the Fitbit app. None of them tell you how their stress score is calculated. Oura calls it "Stress Resilience" and describes it as "your body's ability to recover from stress." Whoop calls it "Strain" and says it measures "the cardiovascular load on your body." Fitbit calls it "Stress Management" and says it uses "electrodermal activity." These are marketing descriptions. They are not algorithms.

Pulsyn's algorithm lives in lib/core/analytics/stress/stress_fusion_service.dart. It is 465 lines. It is not a machine learning model. It is a weighted fusion of six physiological signals with explicit normalization for circadian rhythm. The code is open source. The weights are hardcoded as constants. The app computes a confidence score based on how much data you actually have. If you have not worn the ring in the last six hours, the app says "Unknown" instead of inventing a number.

This post is about what those 465 lines actually do.

A conceptual visualization of stress physiology showing the interconnected nervous system response that wearable devices attempt to measure

The 35% that comes from the ring

The Pulsyn Rune 1 computes a raw stress estimate from the PPG signal on the ring itself. The exact firmware implementation is not ours. The ring is based on the Colmi R09 platform, and the stress value comes from the vendor's PPG analysis pipeline. We treat it as one input among many, not as the truth.

The ring stress is weighted at 35% of the final score. This is a deliberate choice. If the ring's PPG algorithm is having a bad day because your finger is cold or the ring is loose, its opinion is still heard, but it cannot override the other five physiological signals. If you are missing HRV data because the ring could not get a clean reading, the ring stress drops to 0% and the algorithm rebalances around what remains. The normalization happens automatically. The totalWeight variable in the fusion service tracks which sources are present, and the final score is weightedSum / totalWeight with a clamp to 0-100.

This is why the Pulsyn stress score can return "Unknown." If you have no ring stress, no HRV, no heart rate, no sleep data, no temperature, and no SpO2, the app returns StressState.unknown with a score of 0. Most wearables would still give you a number. We would rather be honest.

The 65% that comes from physiology

The Pulsyn algorithm portion is 65% of the total score. It breaks down into five sub-components with their own relative weights:

HRV: 25% of total (38.5% of Pulsyn weight)

HRV is the most important signal because it is the closest proxy we have for autonomic nervous system balance. High HRV relative to your baseline means your parasympathetic system is dominant. Low HRV means sympathetic activation. The mapping is linear and piecewise:

  • 20% or more above baseline = 0 stress (excellent recovery)
  • At baseline = 30 stress (normal)
  • 20% below baseline = 60 stress (elevated)
  • 40% below baseline = 85 stress (high)
  • 60% or more below baseline = 100 stress (critical)

The code lives in _calculateHRVStress at lines 340-355 of stress_fusion_service.dart. It is not a neural network. It is five if statements and a percentage change calculation.

But HRV is not constant across the day. Your HRV at 3 AM is naturally lower than your HRV at 1 PM because your autonomic tone shifts with your circadian rhythm. If you compare a midday HRV reading against a baseline built from morning readings, you will get false positives. The fusion service handles this with a circadian modifier table that has 24 entries, one for each hour. The modifier at 3 AM is 0.78. The modifier at 1 PM is 1.12. Before the HRV stress component is computed, the current HRV is divided by the modifier for the current hour. This normalizes the value to a baseline-neutral time of day.

A medical heart rate variability chart showing the kind of waveform data that Pulsyn uses for stress calculation instead of opaque machine learning

Heart rate: 20% of total (30.8% of Pulsyn weight)

Heart rate elevation above resting is a direct measure of sympathetic activation. The mapping is:

  • At or below resting = 0 to 10 stress
  • 10% above resting = 20 stress
  • 20% above resting = 40 stress
  • 30% above resting = 60 stress
  • 50% or more above resting = 90+ stress

This is _calculateHeartRateStress at lines 357-375. The resting heart rate is your personal baseline, computed by Pulsyn over the first two weeks of wear and updated continuously. A heart rate of 85 BPM is not inherently stressful. A heart rate of 85 BPM when your resting is 55 BPM is.

Sleep: 12% of total (18.5% of Pulsyn weight)

Sleep quality is estimated from the last sleep session's stage distribution. The algorithm checks deep sleep percentage, REM percentage, and awake percentage against optimal ranges. Deep sleep should be 15 to 20% of total sleep. REM should be 20 to 25%. Awake time should be under 5%. The quality score feeds into a duration component that penalizes both short sleep (under 5 hours = 70 stress) and excessive sleep (over 10 hours = 30 stress). The final sleep stress is 60% quality and 40% duration.

This is _calculateSleepStress at lines 377-398. It uses the same sleep data that Pulsyn already computed for your sleep score. There is no separate sleep sensor. The ring measured the sleep. The app scored it. The stress algorithm just reads the existing result.

Temperature: 5% of total (7.7% of Pulsyn weight)

Skin temperature deviation from baseline is a small but reliable signal. The mapping is based on Celsius deviation:

  • 0 to 0.2 degrees = 0 to 10 stress
  • 0.2 to 0.5 degrees = 10 to 30 stress
  • 0.5 to 1.0 degrees = 30 to 60 stress
  • 1.0+ degrees = 60 to 100 stress

This is _calculateTemperatureStress at lines 400-408. The low weight reflects the fact that skin temperature is noisy. It is affected by room temperature, air conditioning, and whether you just washed your hands. But sustained elevation of 0.5 degrees or more is a real physiological signal, and the algorithm includes it at a weight that prevents it from dominating the score.

SpO2: 3% of total (4.6% of Pulsyn weight)

Blood oxygen gets the smallest weight because smart ring SpO2 is the least reliable metric in the entire signal chain. The PPG post from last week explained why. The sensor geometry in a ring is uncontrolled. The optical path length is variable. The algorithm treats SpO2 as a canary, not a judge. If your SpO2 is under 92%, the component adds 70+ stress. If it is 98% or above, it adds 0. The weight is only 3%, so a single bad SpO2 reading cannot spike your whole score. But if it stays low, it will pull the average up.

A data visualization showing the algorithmic fusion of multiple physiological signals into a single stress metric

Why the weights are what they are

The 35/65 split between ring stress and Pulsyn algorithm is a hedge against the ring's black box. The Colmi R09 firmware computes stress from the PPG signal using its own undisclosed method. We include it because it is a real signal. We do not trust it completely because we cannot audit it. Giving it 35% means it matters, but the other 65% can override it if the physiological data disagrees.

Within the Pulsyn algorithm, HRV gets the largest share because it is the most validated stress biomarker in the academic literature. The MDPI 2025 HRV stress correlation meta-analysis is cited in the source code comments. Heart rate gets the second-largest share because it is the most immediately responsive. Sleep gets 12% because it is a lagging indicator. Yesterday's bad sleep affects today's stress, but it does not tell you what is happening right now. Temperature and SpO2 get the remainder because they are secondary signals with lower signal-to-noise ratios.

I am not claiming these weights are optimal. They are the best balance we have found between responsiveness and stability. We will adjust them as we get more user data. The weights are constants, not learned parameters, so changing them requires a code update. This is intentional. We do not want the stress algorithm to drift in ways we cannot explain.

The six states and what they mean

The final score maps to one of six states:

  • Optimal (0-29): Relaxed, recovered state. Your HRV is at or above baseline, your heart rate is at or below resting, and your sleep was adequate. This is not a challenge. This is the baseline you are aiming for.
  • Moderate (30-49): Normal daily stress. You are awake, you are active, your body is handling load. Most people will spend most of their day here.
  • Elevated (50-69): Above normal. Your HRV is depressed, your heart rate is elevated, or your sleep was poor. This is a signal to pay attention, not an emergency.
  • High (70-84): High stress. Multiple signals are elevated. Intervention is recommended. This could mean rest, hydration, or simply stopping what you are doing.
  • Severe (85-100): Critical stress. Your physiology is showing significant strain. This does not mean you are having a medical emergency. It means your autonomic system is heavily loaded.
  • Unknown: No data available. The app will not guess.

The confidence score is totalWeight, which ranges from 0.0 to 1.0. A score computed with all six sources has 100% confidence. A score computed with only ring stress and heart rate has 55% confidence. The app logs this internally. We may expose it in a future update.

What this does not do

I want to be clear about the limitations. The Pulsyn stress score is not a medical diagnosis. It is a fused physiological index. It cannot tell the difference between physical stress (you just ran a 5K) and psychological stress (you have a deadline). Both elevate heart rate and depress HRV. The algorithm does not know which one it is.

It also cannot detect acute stress events in real time with the speed of an EEG or a galvanic skin response sensor. The ring samples PPG periodically, not continuously. The HRV analysis requires a window of inter-beat intervals. The fastest the score can update is limited by the BLE sync interval and the processing time on the phone. This is a wellness metric, not a biofeedback tool.

Finally, the score is only as good as your baseline. The first two weeks of wear are critical. If you start wearing the ring during a period of high stress, the baseline will be skewed high, and subsequent normal readings will look artificially good. The algorithm recomputes the baseline continuously with a decay factor, but the initial calibration matters.

The debug view you will never see

The fusion service computes a second score internally called pulsynStress. This is the 65% algorithm portion without the ring's 35% input. It exists for debugging and comparison. If the ring says you are at 80 stress and the Pulsyn algorithm says you are at 35, the fusion score will land around 51, but the divergence is logged. We use this to catch firmware anomalies. You do not see this in the app. It is there so we can improve the algorithm without shipping a black box.

An anatomical diagram of the autonomic nervous system illustrating the physiological pathways that produce measurable stress biomarkers like HRV and heart rate elevation

I am also uncertain about the circadian modifier table. The values are based on population averages from the literature. Your personal HRV curve might peak at 11 AM instead of 1 PM. The 0.78 modifier at 3 AM might be too aggressive for night shift workers. We have not validated the table across enough demographics. This is the part of the algorithm I am most likely to change.

What the competitors do

Oura's "Stress Resilience" score is part of their $5.99 monthly subscription. The algorithm is not published. The weights are unknown. The user has no way to know whether the score is based on HRV, heart rate, skin temperature, or a combination of all three. Oura's documentation says it "measures your body's ability to recover from stress throughout the day." This is a description of intent, not a description of method.

Whoop's "Strain" score is similarly opaque. It is based on cardiovascular load, but the exact formula is proprietary. Whoop's score ranges from 0 to 21, which is a smaller range than Pulsyn's 0-100. This compresses the dynamic range and makes small changes harder to detect. Whoop also requires a $30 monthly subscription.

Fitbit's "Stress Management" score uses electrodermal activity on devices that have an EDA sensor. Most Fitbit devices do not have this sensor. The ones that do charge a $9.99 monthly subscription for detailed breakdowns. The score is not available on the basic free tier.

Pulsyn does not charge a subscription. The stress score is available on the free tier. The algorithm is in the repository. The weights are in the code. The app tells you when it does not have enough data. This is not a feature. It is the minimum standard for any health metric that claims to be transparent.


About the author

James Hoffmann is the founder of Pulsyn. He has been reverse-engineering BLE health devices and their data pipelines for two years. The stress fusion algorithm was the fifth iteration of Pulsyn's stress scoring system.


References

  1. Pulsyn stress_fusion_service.dart, lib/core/analytics/stress/. Fusion algorithm with circadian normalization and weighted component scoring.
  2. MDPI 2025 HRV stress correlation meta-analysis. Cited in source code comments for HRV weight justification.
  3. Oura Ring Stress Resilience documentation. https://support.ouraring.com/hc/en-us/articles/Stress-Resilience (accessed June 2026).
  4. WHOOP Strain documentation. https://www.whoop.com/us/en/thelocker/strain/ (accessed June 2026).
  5. Fitbit Stress Management documentation. https://www.fitbit.com/global/us/technology/stress (accessed June 2026).