Moderate burnout risk detected — signals trending above baseline for 4 days
Heart rate variability
38
ms — baseline 55ms
-31% below baseline
Sleep last night
5.2
hrs — baseline 7.5hrs
-31% below baseline
Resting heart rate
74
bpm — baseline 62bpm
+19% above baseline
61
/ 100
Moderate Risk
7-day rolling average. Primary driver: HRV suppression + sleep deficit
7-day burnout risk trend
Mon Tue Wed Thu Fri Sat Today
Signal breakdown — what's driving the score
💓
HRV stress signal (weight: 35%)
72%
🌙
Sleep deficit (weight: 25%)
58%
❤️
Resting HR elevation (weight: 25%)
44%
👣
Activity drop (weight: 15%)
28%
Wearable signal simulator
Adjust readings to see real-time burnout risk calculation
💓 HRV (Heart Rate Variability) 38 ms
Baseline: 55ms — lower = more stressed
🌙 Sleep duration 5.2 hrs
Baseline: 7.5hrs — less sleep = higher risk
❤️ Resting heart rate 74 bpm
Baseline: 62bpm — higher = more strain
👣 Daily steps 5,200
Baseline: 8,000 steps — drop = disengagement
Burnout risk score
61
Moderate risk
Watch trend over next 3 days. Recommend rest and reduced workload.
Signal contributions
HRV stress
60%
Sleep deficit
25%
HR elevation
10%
Activity drop
5%
biomarker_engine.py
Core ML
import numpy as np
from dataclasses import dataclass
from typing import List, Dict

@dataclass
class WearableReading:
    hrv_ms: float       # Heart Rate Variability in milliseconds
    sleep_hrs: float    # Sleep duration in hours
    rhr_bpm: int        # Resting Heart Rate in bpm
    steps: int          # Daily step count

class BurnoutBiomarker:
    BASELINES = {"hrv": 55, "sleep": 7.5, "rhr": 62, "steps": 8000}
    WEIGHTS   = {"hrv": 0.35, "sleep": 0.25, "rhr": 0.25, "steps": 0.15}

    def personalise(self, user_readings: List[WearableReading]):
        """Override population baselines with user's own 30-day avg."""
        if len(user_readings) >= 30:
            self.BASELINES["hrv"]   = np.mean([r.hrv_ms for r in user_readings])
            self.BASELINES["sleep"] = np.mean([r.sleep_hrs for r in user_readings])
            self.BASELINES["rhr"]   = np.mean([r.rhr_bpm for r in user_readings])
            self.BASELINES["steps"] = np.mean([r.steps for r in user_readings])

    def score_day(self, r: WearableReading) -> Dict:
        signals = {
            "hrv"  : (self.BASELINES["hrv"]   - r.hrv_ms)    / self.BASELINES["hrv"],
            "sleep": (self.BASELINES["sleep"] - r.sleep_hrs) / self.BASELINES["sleep"],
            "rhr"  : (r.rhr_bpm - self.BASELINES["rhr"])    / self.BASELINES["rhr"],
            "steps": (self.BASELINES["steps"] - r.steps)    / self.BASELINES["steps"],
        }
        raw  = sum(self.WEIGHTS[k] * max(0, v) for k,v in signals.items())
        risk = round(min(100, raw * 100), 1)
        return {"risk": risk, "signals": signals}

    def rolling_risk(self, days: List[WearableReading]) -> float:
        return round(np.mean([self.score_day(d)["risk"] for d in days[-7:]]), 1)

    def interpret(self, score: float) -> str:
        if score >= 70: return "High — escalate to clinician"
        if score >= 45: return "Moderate — recommend check-in"
        return "Low — continue monitoring"
data_connectors.py
Data Layer
import fitbit, json, xml.etree.ElementTree as ET

class FitbitConnector:
    def __init__(self, access_token: str):
        self.client = fitbit.Fitbit(access_token=access_token)

    def get_today(self) -> dict:
        hrv   = self.client.intraday_time_series('activities/heart', detail_level='1min')
        sleep = self.client.get_sleep(date='today')
        steps = self.client.activities()['summary']['steps']
        return {"hrv": hrv, "sleep": sleep["summary"]["totalTimeInBed"]/60, "steps": steps}

class AppleHealthConnector:
    def parse_export(self, xml_path: str) -> list:
        tree = ET.parse(xml_path)
        records = tree.getroot().findall("Record")
        hrv_records = [r for r in records
                       if r.get("type") == "HKQuantityTypeIdentifierHeartRateVariabilitySDNN"]
        return [{"value": float(r.get("value")),
                  "date": r.get("startDate")} for r in hrv_records]

# Option 3: WESAD academic dataset (free, validated)
# archive.ics.uci.edu/dataset/465/wesad
def load_wesad(subject_id: int) -> list:
    import pickle
    with open(f"WESAD/S{subject_id}/S{subject_id}.pkl", "rb") as f:
        data = pickle.load(f, encoding="latin1")
    return data["signal"]["wrist"]

What is VitalMind?

VitalMind is a digital biomarker platform that detects early burnout risk using wearable signals — before a person consciously feels it.

It combines heart rate variability, sleep quality, resting heart rate and activity data into a single personalised risk score, updated daily.

Unlike population-based apps, VitalMind compares you against your own 30-day baseline — making it accurate for each individual.

PythonNumPyFitbit APIApple HealthWESAD datasetFlaskReact

How it works

1
Collect wearable signals
HRV, sleep, resting HR, steps — pulled automatically from Fitbit or Apple Health daily.
2
Normalize to personal baseline
Each signal is compared against the user's own 30-day rolling average — not population norms.
3
Compute composite score
Weighted formula: HRV 35% + Sleep 25% + RHR 25% + Steps 15% → 0–100 risk score.
4
7-day rolling window
Daily scores averaged over 7 days to detect sustained patterns, not just bad days.
5
Explainable alert
Every alert shows which signal is driving the score — so clinicians know exactly where to look.

Why this matters

76%
of workers report burnout — most undetected until crisis
14 days
average early warning window wearables can provide
$322B
annual global cost of burnout-related turnover