A Garmin Connect IQ widget that provides real-time biofeedback on your affective state using Russell's Circumplex Model. It analyzes heart rate and heart rate variability (HRV) to map your physiological state onto two dimensions: Arousal (energy/activation) and Valence (engagement vs stress).
- Real-Time Affective State Tracking: Continuous analysis of heart rate and HRV (RMSSD) to map your current state
- 12 Distinct States: From "Serene" to "Thriving" to "Intense" - detailed emotional state classification
- Personal Calibration: 2-minute baseline calibration for accurate, individualized readings
- Threat–Challenge Model: Distinguishes between positive high-arousal (excitement/engagement) and negative high-arousal (stress/tension)
- Predictive Vibration Alerts: Early warning system detects trajectory toward extreme states and provides haptic nudges before overwhelm
- Multiple View Modes: Three distinct screens - cinematic emotion view, detailed metrics, and supportive guidance messages
- Alexithymia Support: Body-focused language ("heart active, body tight") instead of abstract emotion labels
- Visual Instability Rendering: Dynamic visualization with organic jitter that mirrors your physiological tension in real-time
- Interactive Controls: Tap to cycle through views, long-press to recalibrate
- Memory-Efficient: Designed for Garmin's strict memory constraints (~600 bytes total buffer usage)
- Glanceable Widget: Quick check-ins without launching a full app
- Visual Circumplex Display: See your position on the arousal-valence plane at a glance
- Persistent Calibration: Baseline stored across sessions - no need to recalibrate constantly
- 75+ Compatible Devices: Works on Fenix, Forerunner, Venu, Vivoactive, Epix, and more
The Affect Engine continuously analyzes biometric data to provide live affective biofeedback:
High Arousal
|
Stressed | Engaged
(tense) | (excited)
|
─────────────────┼─────────────────
Negative | Positive
Valence | Valence
|
Tired | Calm
(depleted) | (restored)
|
Low Arousal
This reframes Russell's circumplex using a Threat–Challenge model that better maps to what HR + HRV can distinguish physiologically.
The app classifies your state into one of 12 labels based on your position in the arousal-valence space. Each state has three intensity levels:
"Challenge Response" - High activation with parasympathetic tone preserved
| State | Arousal | Valence | Description | When You'll See This |
|---|---|---|---|---|
| Engaged | Medium-High | Slightly Positive | Focused and alert | Working on an interesting problem |
| Excited | High | Positive | Energized and enthusiastic | Approaching finish line, anticipation |
| Thriving | Very High | Very Positive | Peak performance state | Flow state, optimal challenge |
Physiology: Heart rate elevated BUT HRV (RMSSD) preserved → parasympathetic system still engaged
"Threat Response" - High activation with parasympathetic withdrawal
| State | Arousal | Valence | Description | When You'll See This |
|---|---|---|---|---|
| Tense | Medium-High | Slightly Negative | Uncomfortable activation | Tight deadline, mild pressure |
| Stressed | High | Negative | Overwhelmed and pressured | Difficult workout, conflict |
| Intense | Very High | Very Negative | Extreme stress response | Fight-or-flight, panic, overtraining |
Physiology: Heart rate elevated AND HRV (RMSSD) suppressed → parasympathetic withdrawal, stress response
"Recovery State" - Low activation with good HRV
| State | Arousal | Valence | Description | When You'll See This |
|---|---|---|---|---|
| Still | Medium-Low | Slightly Positive | Quiet and present | Light meditation, easy breathing |
| Calm | Low | Positive | Relaxed and at peace | Deep relaxation, after-workout rest |
| Serene | Very Low | Very Positive | Deeply restored | Deep meditation, restorative sleep |
Physiology: Heart rate low AND HRV (RMSSD) high → full parasympathetic dominance, recovery mode
"Depletion State" - Low activation with poor HRV
| State | Arousal | Valence | Description | When You'll See This |
|---|---|---|---|---|
| Resting | Medium-Low | Slightly Negative | Quietly depleted | End of long day, low energy |
| Tired | Low | Negative | Fatigued and drained | Exhaustion, overtraining |
| Drained | Very Low | Very Negative | Severe depletion | Burnout, illness, severe fatigue |
Physiology: Heart rate low BUT HRV (RMSSD) also low → parasympathetic system depleted
The widget shows:
-
Position Dot: Your current location on the circumplex plane
- Horizontal axis: Valence (left = negative, right = positive)
- Vertical axis: Arousal (bottom = low, top = high)
-
State Label: One of the 12 states listed above (e.g., "Calm", "Excited", "Stressed")
-
Numerical Values:
- Arousal: -1.0 (lowest) to +1.0 (highest activation)
- Valence: -1.0 (most negative) to +1.0 (most positive)
-
Raw Metrics (for transparency):
- HR: Current heart rate in BPM
- RMSSD: Root mean square of successive RR interval differences (milliseconds)
- Higher RMSSD = better HRV = more parasympathetic tone
Good Signs:
- Excited/Thriving during exercise → optimal challenge level
- Calm/Serene during rest → effective recovery
- Moving from Stressed to Engaged → adapting to challenge
- Moving from Tired to Calm → recovering properly
- Smooth orb visuals → stable autonomic state
- Single vibration followed by recovery → successfully caught and managed rising stress
Warning Signs:
- Stuck in Stressed/Intense → may need to reduce load
- Tired/Drained after rest → possible overtraining or illness
- Tense during intended relaxation → stress not resolving
- Very low RMSSD (<20ms) for extended periods → autonomic dysfunction
- Frequent vibration alerts → consistently hitting extreme states (consider intervention)
- Jittery, spiked orb → high physiological instability
Understanding Vibration Alerts: The app uses a predictive early warning system:
- Single short pulse: "Approaching" alert - you're trending toward stress but not there yet
- Double pulse: "Warning" - you're in elevated stress territory and it's rising
- Triple pulse: "Urgent" - you're in extreme stress zone
- Soft long pulse: "Recovery" - positive feedback when you've come down from an extreme state
- 5-minute cooldown: Between alerts to avoid notification fatigue
Remember: This is physiology-driven biofeedback, not mind-reading. External factors (caffeine, temperature, altitude, illness, medication) all affect these metrics.
✅ What it IS:
- Physiology-driven biofeedback tool that shows your autonomic nervous system state
- Training aid to help you recognize your stress vs recovery patterns
- Challenge-response monitor to distinguish excitement from stress
- Recovery tracker to see if your downtime is truly restorative
- Early warning system for alexithymia support - catches rising stress before you consciously notice
- Visual biofeedback with real-time instability rendering that mirrors your physiology
- Accessible for alexithymia - uses body-focused language instead of abstract emotion words
❌ What it is NOT:
- Not an emotion detector - it cannot identify specific emotions like "anger" or "joy"
- Not medical advice - this is informational/entertainment only
- Not a diagnostic tool - does not diagnose anxiety, depression, or other conditions
The app tracks physiological activation and autonomic balance, not subjective feelings. The vibration alerts are predictive nudges, not diagnoses.
- What it measures: Sympathetic vs Parasympathetic nervous system balance
- High Arousal: Elevated HR + Suppressed RMSSD = Stress/Excitement
- Low Arousal: Lower HR + Higher RMSSD = Rest/Calm
- Range: -1.0 (lowest activation) to +1.0 (highest activation)
- What it measures: RMSSD preservation under arousal (Threat vs Challenge)
- Positive Valence: RMSSD maintained despite elevated HR = Challenge response (engaged/recovered)
- Negative Valence: RMSSD suppressed alongside elevated HR = Threat response (stressed/depleted)
- Range: -1.0 (most negative/stressed) to +1.0 (most positive/engaged)
Key Insight: The valence axis doesn't measure "happiness" - it measures whether your parasympathetic nervous system is working WITH your stress response (challenge/engagement) or being shut down by it (threat/depletion).
Arousal correlates with the balance between Sympathetic (SNS) and Parasympathetic (PNS) nervous systems:
- Metrics: HR + RMSSD
- Logic:
- Low RMSSD + High HR = High arousal (stress/excitement)
- High RMSSD + Low HR = Low arousal (rest/relaxation)
- Normalization: user baseline calibration (see Calibration)
The default weighting (per project constants):
Clamped to [-1, 1].
Valence uses RMSSD preservation under arousal to separate stress from excitement:
- Excitement/Challenge: HR elevated but RMSSD preserved (parasympathetic tone maintained)
- Stress/Threat: HR elevated and RMSSD suppressed (parasympathetic withdrawal)
Formula (at high arousal):
Arousal gating: at low arousal, valence reflects pure RMSSD level (recovery vs depletion).
Constants (per project):
TENSION_PENALTY = 0.5— how much elevated HR penalizes valenceAROUSAL_LOW_GATE = 0.15— below this, valence = pure RMSSDAROUSAL_HIGH_GATE = 0.4— above this, full stress vs excitement logic
Garmin Connect IQ apps have strict memory limits. The engine uses:
- Fixed-size circular buffers (e.g., RR buffer ~90 samples)
- Streaming calculations (Welford/online variance)
- Incremental statistics (constant memory)
- No unbounded arrays
Core components (see source/):
RMSSDCalculator.mc: RMSSD from RR intervals using circular bufferHRVStabilityAnalyzer.mc: HRV stability (CV) using online varianceCalibrationManager.mc: baseline calibration with persistent storageCircumplexMapper.mc: maps (HR, RMSSD) → (Arousal, Valence) using threat–challenge modelBiometricCollector.mc: sensor polling (nominally 1 Hz)AffectView.mc: UI rendering (calibration + circumplex)
SensorHistory (RR Intervals)
↓
RMSSDCalculator (every beat / every new RR)
↓
CircumplexMapper (threat–challenge mapping)
↓
(Arousal, Valence) → UI
- Open the Garmin Connect IQ app on your phone
- Search for "Affect"
- Install to your compatible watch
- Add as a widget from your watch's widget menu
- Build a
.prgfile for your device (see Build Instructions below) - Connect your watch via USB
- Copy the
.prgto/GARMIN/APPS/on your watch - Eject and launch the widget
Important: For accurate readings, you must complete the 2-minute calibration on first launch.
- Launch the widget from your watch's widget menu
- Stay calm and still during the 2-minute calibration period
- Sit or stand comfortably
- Breathe normally
- Avoid talking, moving, or fidgeting
- Wait for completion - you'll see a progress bar
- The widget will automatically switch to live tracking mode
- Calibration is saved - you won't need to repeat it unless you reset the app
Why calibration matters: The app uses your personal baseline HR and RMSSD to normalize all readings. Without calibration, the states won't be accurate.
- Swipe to the widget from your watch face
- Glance at your current state: Look at the position dot and state label
- Check the quadrant:
- Top-right (Engaged/Excited) = Good challenge level
- Bottom-right (Calm/Serene) = Good recovery
- Top-left (Tense/Stressed) = Consider backing off
- Bottom-left (Resting/Tired) = Need more recovery
- Tap to switch views:
- Emotion View (default): Cinematic visualization with body-focused labels
- Metrics View: Detailed numbers - HR, HRV, stability %, arousal/valence values
- Guidance View: Supportive/humorous messages tailored to your current state
- Notice vibration alerts: The watch will gently vibrate if you're trending toward extreme stress/depletion
- Review periodically throughout your day to build awareness of your patterns
If you need to recalibrate (e.g., after significant training changes, weight loss/gain, or if readings seem off):
- Go to your watch's app settings
- Find "Affect" in the widget list
- Delete/clear data
- Relaunch the widget to trigger a new calibration
Emotion View (Default Screen):
- Dynamic orb: Center visualization with organic movement reflecting your physiological tension
- Calm states: Smooth, gentle circles
- Tense states: Jittery, spiked edges that literally show the instability
- Body-focused labels: "ACTIVATED", "TENSE", "RELAXED", "DEPLETED" (easier to recognize than abstract emotions)
- Sublabels: "heart active, body coping" / "heart calm, body tight" (alexithymia-friendly)
- Dot position: Your current state on the arousal-valence crosshair
- Color coding: Warm gold (excited), coral-red (stressed), teal (calm), lavender-blue (tired)
Metrics View (Tap once):
- HR: Current heart rate in beats per minute (large display)
- HRV: RMSSD in milliseconds (higher = better parasympathetic tone)
- Stability: Percentage showing HRV consistency
- Arousal value: -1.0 to +1.0 (activation level)
- Valence value: -1.0 to +1.0 (engagement quality)
Guidance View (Tap twice):
- State-appropriate messages: Supportive/humorous text based on your quadrant
- Stressed: "Plot twist: You survive 100% of these."
- Tired: "Horizontal is a valid life choice today."
- Calm/Excited: Positive affirmations
- Rotating phrases: Multiple messages cycle through for variety
Interactive Controls:
- Tap: Cycle through views (Emotion → Metrics → Guidance → Emotion)
- Long Press: Reset and recalibrate
- Time display: Current time shown at bottom of most screens
Quick Interpretation:
- Top-right quadrant = High energy + good HRV = Engaged/Excited
- Top-left quadrant = High energy + poor HRV = Tense/Stressed
- Bottom-right quadrant = Low energy + good HRV = Calm/Serene
- Bottom-left quadrant = Low energy + poor HRV = Resting/Tired
Previous documentation format (click to expand)
- Launch widget
- Stay calm and still during calibration
- Baseline HR and RMSSD are calculated and stored
- Widget switches to live tracking
- Dot: current state on circumplex
- Quadrant labels: Engaged/Stressed/Calm/Tired (with intensity variants like Thriving, Intense, Serene, Drained)
- Numeric values: Arousal (-1..+1), Valence (-1..+1)
- Metrics: HR and RMSSD displayed for transparency
Baseline is stored in persistent properties. To recalibrate, clear the app’s stored data (or uninstall/reinstall).
Platform Requirements:
- App type: Widget (glanceable, always accessible)
- Minimum API: Connect IQ 3.2.0+ (required for
SensorHistoryAPI) - Developed with: Connect IQ SDK 8.4.0+
Compatible Device Families (75+ devices):
- ✅ Fenix 7/8 series
- ✅ Epix 2 / Epix Pro
- ✅ Forerunner: 165, 255, 265, 570, 955, 965, 970
- ✅ Venu 2/3/4 series
- ✅ Vivoactive 5/6
- ✅ Instinct 3 series
- ✅ MARQ Gen 2 series
- ✅ D2 Air / D2 Mach series
- ✅ Enduro 2/3
- ✅ Tactix 7 / Tactix Delta
See manifest.xml for the complete authoritative device list.
Requirements: Device must have:
- Optical heart rate sensor
- Connect IQ 3.2.0+ support
- Enough available memory for widget (~600 bytes runtime)
Recommended: Wait for the Connect IQ Store release (coming soon!)
Alternatively, see Getting Started above for sideloading instructions.
Developer Build Instructions (click to expand)
Important: PRG vs IQ
-
.prg: device-specific build used for simulator and USB sideload testing -
.iq: universal package intended for Connect IQ Store upload
For USB sideload testing on a physical watch, build and copy a .prg.
Prerequisites (Windows)
- Garmin Connect IQ SDK 8.4.0+
- Java (required by the SDK)
- OpenSSL (for developer key generation)
- A developer key (RSA 4096-bit)
Generate Developer Key (DER format expected by monkeyc)
openssl genrsa -out developer_key.pem 4096
openssl pkcs8 -topk8 -inform PEM -outform DER -in developer_key.pem -out developer_key -nocryptBuild Commands (Windows)
If you use the SDK .bat wrappers, set your SDK path:
$SDK = "C:\Users\user\AppData\Roaming\Garmin\ConnectIQ\Sdks\connectiq-sdk-win-8.4.0-2025-12-03-5122605dc"Build a device-specific PRG for testing/sideloading:
& "$SDK\bin\monkeyc.bat" -f monkey.jungle -o bin\Affect.prg -d fenix7 -y developer_key -wRun in the simulator:
& "$SDK\bin\connectiq.bat"
& "$SDK\bin\monkeydo.bat" bin\Affect.prg fenix7Build a universal IQ for store upload:
& "$SDK\bin\monkeyc.bat" -f monkey.jungle -o bin\Affect.iq -y developer_key -r -wUSB sideload (physical watch testing)
- Build a PRG for your device ID (e.g.,
vivoactive5,fenix7, etc.) - Connect watch via USB (shows as a drive)
- Copy the
.prgto:
/GARMIN/APPS/
- Eject and launch the widget
Helper scripts
This repo includes PowerShell/batch helpers (see project root), e.g.:
-
build-iq.ps1(build package) -
test-*.ps1(device/simulator runs) -
start-simulator.bat,run*.bat
The app is designed with strict memory efficiency for Garmin's constraints (~28-64KB available depending on device):
| Component | Purpose | Memory Strategy |
|---|---|---|
RMSSDCalculator.mc |
RMSSD from RR intervals | 90-element circular buffer |
HRVStabilityAnalyzer.mc |
HRV stability (CV) | Welford's online variance algorithm |
CalibrationManager.mc |
Baseline calibration | Persistent storage (AppStorage) |
CircumplexMapper.mc |
Maps (HR, RMSSD) → (Arousal, Valence) | Stateless computation |
BiometricCollector.mc |
Sensor polling at 1Hz | Direct streaming, no buffering |
AlertManager.mc |
Predictive vibration alerts | 10-sample trend detection with linear regression |
AffectView.mc |
UI rendering with 3 view modes | Minimal draw calls, procedural rendering |
AffectDelegate.mc |
Input handling | Tap/long-press gesture detection |
Total Memory Footprint: ~600 bytes for all buffers
- Fixed-size circular buffers - No dynamic arrays
- Streaming algorithms - Welford's online variance, running sums
- Process and discard - No raw data storage
- Persistent calibration - Baseline stored in properties, not RAM
SensorHistory (RR Intervals from watch)
↓
RMSSDCalculator (every new RR interval)
↓
CircumplexMapper (applies threat–challenge model)
↓
(Arousal, Valence) → UI Rendering
- Physiology ≠ emotion: HR/HRV cannot uniquely identify discrete emotions without context.
- Physiology ≠ emotion: HR/HRV cannot uniquely identify discrete emotions without context.
- Valence is a proxy: RMSSD preservation reflects threat–challenge distinction, not hedonic pleasure.
- Motion artifacts: movement and poor sensor contact can dominate short-term HRV features.
- Breathing effects: paced/slow breathing can raise RMSSD without implying "positive emotion."
- Individual variation: population baselines are used; personal calibration would improve accuracy.
- Sensor quality varies: Optical HR sensors are less accurate than chest straps, especially during movement
- Update frequency: Data updates approximately every second, not real-time
- Calibration dependency: Readings are only as good as your initial calibration conditions
- No emotion specificity: Cannot distinguish "angry" from "excited" or "sad" from "tired"
- ☕ Caffeine/stimulants - Elevate HR and may suppress HRV
- 🌡️ Temperature - Heat/cold affect HR and HRV
- 🏔️ Altitude - Affects HR and respiratory patterns
- 💊 Medications - Many affect heart rate and HRV
- 🤒 Illness - Infection/inflammation suppresses HRV
- 🍺 Alcohol - Suppresses HRV for 12-24 hours after consumption
- 😴 Sleep quality - Poor sleep suppresses HRV the next day
- 💪 Exercise timing - Recent hard exercise suppresses HRV
This app is for informational and entertainment purposes only.
⚠️ Not medical advice - Do not use for diagnosis or treatment⚠️ Not FDA approved - This is not a medical device⚠️ Consult professionals - See a doctor for health concerns⚠️ No warranties - Use at your own risk
- Russell, J. A. (1980). A circumplex model of affect. Journal of Personality and Social Psychology, 39(6), 1161–1178.
- Original circumplex model of affect
- Blascovich, J., & Mendes, W. B. (2000). Challenge and threat appraisals: The role of affective cues. In Feeling and Thinking (pp. 59–82). Cambridge University Press.
- Theoretical basis for threat–challenge distinction
- Shaffer, F., & Ginsberg, J. P. (2017). An Overview of Heart Rate Variability Metrics and Norms. Frontiers in Public Health, 5, 258.
- Comprehensive HRV metrics guide
- Task Force of the European Society of Cardiology (1996). Heart rate variability: standards of measurement, physiological interpretation and clinical use. Circulation, 93(5), 1043–1065.
- Gold standard HRV guidelines
- Porges, S. W. (2011). The Polyvagal Theory: Neurophysiological Foundations of Emotions
- Understanding the vagal nerve's role in emotion regulation
- Appelhans, B. M., & Luecken, L. J. (2006). Heart rate variability as an index of regulated emotional responding. Review of General Psychology, 10(3), 229-240.
- HRV and emotion regulation
Contributions are welcome! Please feel free to submit issues or pull requests.
Areas for contribution:
- Device-specific testing and optimization
- UI/UX improvements
- Additional validation studies
- Documentation improvements
- Translations
MIT License - See LICENSE file for details.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Built with the Garmin Connect IQ SDK. Thanks to the Connect IQ developer community for their support and resources.
Informational/entertainment only. Not medical advice.
Made with ❤️ for the quantified self community



