- Project Overview
- System Architecture
- Metrics Explained
- Calculation Formulas
- Fatigue Detection
- Interpretation Guide
- Privacy & Security
- Installation & Usage
- Troubleshooting
- Advanced Configuration
The Behavioral Monitoring System is a Python-based application that tracks and analyzes user interaction patterns (typing and mouse behavior) to provide real-time insights into:
- Productivity levels
- Fatigue indicators
- Typing health metrics
- Work patterns and rhythms
✅ Privacy-Preserving: Never stores actual keystrokes or typed content
✅ Real-Time Analysis: Continuous metric updates every second
✅ Comprehensive Metrics: 18+ different behavioral indicators
✅ Fatigue Detection: Automatic alerts for unhealthy work patterns
✅ Export Capabilities: JSON data export for further analysis
- 💼 Remote Work Monitoring: Track productivity during WFH
- 🏥 Health & Ergonomics: Detect RSI risks and fatigue patterns
- 📊 Personal Analytics: Understand your work rhythms
- 🎯 Focus Optimization: Identify distraction patterns
- ⏱️ Time Management: Analyze active vs. idle time
┌─────────────────────────────────────────────────────────┐
│ Behavioral Monitor │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Keyboard │ │ Mouse │ │ App │ │
│ │ Listener │ │ Listener │ │ Monitor │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Event Processing Queue │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Analysis Engine (1s interval) │ │
│ │ • Calculate Typing Metrics │ │
│ │ • Calculate Mouse Metrics │ │
│ │ • Detect Fatigue Indicators │ │
│ │ • Clean Old Data │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Metrics Storage │ │
│ │ • Keystroke Data (deque, max 10k) │ │
│ │ • Mouse Data (deque, max 10k) │ │
│ │ • Current Metrics │ │
│ │ • App Usage Statistics │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Export & Reporting │ │
│ │ • JSON Export │ │
│ │ • Real-time Console Updates │ │
│ │ • Fatigue Alerts │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
- Input: Keyboard/mouse events captured by
pynputlibrary - Classification: Events categorized (letter/number/modifier/move/click)
- Queuing: Thread-safe queue stores events temporarily
- Processing: Analysis thread processes queue every 1 second
- Calculation: Metrics computed using rolling window (default 60s)
- Storage: Results stored in deque structures with automatic cleanup
- Output: Metrics available via API calls or console display
Definition: Standard typing speed measurement
Formula:
chars_per_minute = (total_keystrokes / time_span) * 60
wpm = chars_per_minute / 5 # Average word = 5 charactersInterpretation:
< 20 WPM: Very slow (reading/thinking)20-40 WPM: Below average40-60 WPM: Average typing60-80 WPM: Good typing> 80 WPM: Excellent typing
Definition: Typing speed without error correction
Note: Currently same as WPM (system doesn't track errors)
Future Enhancement: Would subtract errors/corrections
Definition: Quality of typing based on pause patterns
Formula:
accuracy_score = 100 - (pause_frequency / 2) - (keystroke_variance * 100)
accuracy_score = clamp(0, 100)Factors:
- Fewer pauses = higher accuracy
- Lower variance = more consistent = higher accuracy
Interpretation:
90-100: Excellent accuracy70-89: Good accuracy50-69: Moderate accuracy< 50: Poor accuracy (many hesitations)
Definition: How steady your typing rhythm is
Formula:
intervals = [time between consecutive keystrokes]
variance = variance(intervals)
max_variance = 0.5 # 500ms expected max
rhythm_consistency = max(0, 100 - (variance / max_variance) * 100)Interpretation:
80-100: Very consistent (in "flow" state)60-79: Good consistency40-59: Moderate consistency< 40: Irregular (distracted/uncertain)
Use Case: Detect when you're "in the zone" vs. struggling
Definition: Composite indicator of typing tiredness
Formula:
baseline_wpm = 40 # Configurable
wpm_factor = max(0, (baseline - actual_wpm) / baseline) * 100
variance_factor = min(100, (keystroke_variance / 0.5) * 100)
fatigue_score = (wpm_factor + variance_factor + pause_frequency) / 3Components:
- Speed degradation: How much slower than baseline
- Rhythm irregularity: Increased variance
- Pause frequency: More thinking/hesitation
Interpretation:
0-30: Fresh and alert31-50: Mild fatigue51-70: Moderate fatigue (consider break)> 70: High fatigue (break recommended)
Definition: Overall typing wellness indicator
Formula:
health_score = 100 - fatigue_score + (rhythm_consistency - 50) / 2
health_score = clamp(0, 100)Interpretation:
80-100: Healthy typing patterns60-79: Acceptable40-59: Warning zone< 40: Unhealthy patterns (risk of RSI)
Definition: Mean time between consecutive keystrokes
Formula:
intervals = [t[i+1] - t[i] for all keystrokes]
avg_interval = mean(intervals)Typical Values:
0.1-0.2s: Fast typing (60-120 WPM)0.3-0.5s: Normal typing (40-60 WPM)> 1.0s: Slow/thoughtful typing
Definition: Variability in typing rhythm
Formula:
variance = variance(keystroke_intervals)Interpretation:
< 0.1: Very consistent0.1-0.3: Normal variation> 0.5: Highly irregular (distracted/tired)
Definition: Percentage of long gaps in typing
Formula:
pauses = count(intervals > 2.0 seconds)
pause_frequency = (pauses / total_intervals) * 100Interpretation:
< 10%: Continuous flow10-20%: Normal thinking pauses20-40%: Frequent breaks in thought> 40%: Highly fragmented (multitasking?)
Definition: Number of rapid typing sequences
Formula:
burst_threshold = 0.1 # 100ms between keys
consecutive_fast = 0
bursts = 0
for interval in intervals:
if interval < burst_threshold:
consecutive_fast += 1
else:
if consecutive_fast >= 3: # At least 3 fast keys
bursts += 1
consecutive_fast = 0Interpretation:
0-2: Slow, methodical typing3-10: Normal bursts> 10: High-speed typing with flow states
Definition: Cumulative mouse movement
Formula:
distance = 0
for i in range(1, len(mouse_positions)):
dx = positions[i].x - positions[i-1].x
dy = positions[i].y - positions[i-1].y
distance += sqrt(dx² + dy²) # Euclidean distanceTypical Values:
< 500px: Minimal movement (focused work)500-2000px: Normal usage> 5000px: High activity (design work, browsing)
Definition: Mean mouse movement velocity
Formula:
avg_speed = total_distance / time_spanInterpretation:
< 50 px/s: Slow, precise movements50-150 px/s: Normal usage> 150 px/s: Fast, possibly erratic
Definition: Rate of mouse clicks
Formula:
clicks = count(click_events)
click_frequency = (clicks / analysis_window) * 60Typical Values:
< 5/min: Reading/viewing5-20/min: Normal work> 30/min: Intense interaction (gaming, data entry)
Definition: Rate of scrolling actions
Formula:
scrolls = count(scroll_events)
scroll_frequency = (scrolls / analysis_window) * 60Use Case: Distinguish reading (high scroll) vs. editing (low scroll)
Definition: How fluid mouse movements are
Formula:
# Calculate angle between consecutive movement vectors
for i in range(1, len(movements) - 1):
vec1 = (dx1, dy1)
vec2 = (dx2, dy2)
cos_angle = dot_product(vec1, vec2) / (magnitude1 * magnitude2)
if cos_angle < 0.5: # > 60° direction change
direction_changes += 1
smoothness = 100 - (direction_changes / movements) * 100Interpretation:
80-100: Smooth, confident movements60-79: Normal usage40-59: Jerky (fatigue or frustration)< 40: Erratic (possible tremor/stress)
Health Indicator: Sudden drops may indicate physical strain
Definition: Count of inactivity gaps > 5 seconds
Formula:
idle_periods = 0
for i in range(1, len(mouse_events)):
gap = events[i].timestamp - events[i-1].timestamp
if gap > 5.0:
idle_periods += 1Use Case: Detect context switching, phone calls, meetings
Definition: Proportion of time with mouse activity
Formula:
idle_time = idle_periods * 5 # Approximate
active_time = time_span - idle_time
active_percentage = (active_time / time_span) * 100Interpretation:
> 70%: High engagement50-70%: Normal work pattern30-50%: Interrupted work< 30%: Mostly idle (meeting, reading)
mean = sum(values) / count(values)variance = sum((x - mean)² for x in values) / count(values)std_dev = sqrt(variance)dot_product = (x1 * x2) + (y1 * y2)distance = sqrt((x2 - x1)² + (y2 - y1)²)Formula:
typing_fatigue_score = typing_metrics.fatigue_score
mouse_inactivity = 100 - mouse_metrics.active_time_percentage
overall_fatigue = (typing_fatigue_score + mouse_inactivity) / 2{
'low_wpm': wpm < 30,
'high_pause_frequency': pause_frequency > 20,
'irregular_rhythm': rhythm_consistency < 50,
'high_fatigue_score': fatigue_score > 70
}{
'low_activity': active_time_percentage < 30,
'jerky_movements': movement_smoothness < 50,
'excessive_idle': idle_periods > 5
}| Level | Score | Recommendation |
|---|---|---|
| Minimal | 0-30 | Continue working |
| Mild | 31-50 | Consider break in 30 min |
| Moderate | 51-70 | Take 5-10 min break soon |
| High | 71-85 | Take break immediately |
| Severe | 86-100 | Stop work, rest required |
{
"session_duration": 172.44,
"typing_metrics": {
"wpm": 2.15,
"health_score": 0,
"fatigue_score": 81.54,
"rhythm_consistency": 0,
"pause_frequency": 50.0
},
"mouse_metrics": {
"total_distance": 1344.22,
"avg_speed": 78.34,
"movement_smoothness": 91.49,
"active_time_percentage": 70.86
}
}Typing Behavior:
- ❌ Very Low WPM (2.15): Minimal typing activity
- ❌ Zero Health Score: Critical - indicates test/minimal usage
- ❌ High Fatigue (81.54): Triggered by low activity
⚠️ 50% Pause Frequency: Half the time spent waiting
Mouse Behavior:
- ✅ Good Distance (1344px): Reasonable exploration
- ✅ Smooth Movements (91.49): Confident, not jerky
- ✅ Active Time (70.86%): Engaged with interface
Conclusion: This is a test session or browsing activity with minimal typing. Not indicative of actual work fatigue.
wpm: 45
health_score: 75
rhythm_consistency: 82
typing_bursts: 8
pause_frequency: 15Analysis: Developer in flow state, consistent rhythm, healthy bursts of code writing.
wpm: 22 # Down from usual 45
health_score: 35
rhythm_consistency: 48
pause_frequency: 38
mouse_smoothness: 52 # Down from usual 85Analysis: Clear fatigue signs - slow, irregular typing, frequent pauses, jerky mouse. Break recommended.
wpm: 5
scroll_frequency: 45
click_frequency: 8
active_time: 85%
typing_bursts: 0Analysis: High engagement (mouse active), minimal typing, high scrolling. Not fatigued, just different task.
❌ Actual keystrokes (which keys pressed)
❌ Typed content (passwords, documents, messages)
❌ Screenshots
❌ Window titles (in current implementation)
❌ Application-specific data
✅ Keystroke timing (when keys pressed)
✅ Key type (letter/number/special/modifier)
✅ Mouse coordinates (x, y positions)
✅ Event timestamps
✅ Calculated metrics (WPM, fatigue, etc.)
- Default: 1 hour rolling window
- Maximum storage: 10,000 keystrokes + 10,000 mouse events
- Auto-cleanup: Old data automatically purged
- Export format: JSON (no sensitive content)
- Don't share raw JSON exports (contains timing patterns)
- Run locally (no network transmission)
- Use for personal analytics only
- Review exported data before sharing
- Comply with local laws (some jurisdictions restrict monitoring)
Python 3.7+
pynput >= 1.7.6
psutil >= 5.9.0# Clone/download the project
cd behavioral-monitor
# Install dependencies
pip install -r requirements.txt
# Test installation
python test_script.py# Grant accessibility permissions
System Preferences → Security & Privacy → Accessibility
# Add Terminal or Python to allowed apps# Install X11 libraries
sudo apt-get install python3-xlib # Ubuntu/Debian# Run as Administrator
# May need to add to antivirus exclusionsfrom behavioral_monitor import BehavioralMonitor
# Create monitor instance
monitor = BehavioralMonitor(
analysis_window=60, # Analyze last 60 seconds
inactivity_threshold=30, # 30s = inactive
data_retention=3600 # Keep 1 hour of data
)
# Start monitoring
monitor.start_monitoring()
# Get current metrics
metrics = monitor.get_current_metrics()
print(f"WPM: {metrics['typing_metrics']['wpm']}")
# Check fatigue
fatigue = monitor.get_fatigue_indicators()
if fatigue['overall_fatigue_level'] > 70:
print("High fatigue detected! Take a break.")
# Export data
monitor.export_data("session_data.json")
# Stop monitoring
monitor.stop_monitoring()# Run with defaults
python behavioral_monitor.py
# Monitor will run until Ctrl+C
# Data exported automatically on exitSymptoms: All metrics show 0
Solutions:
- Check accessibility permissions (macOS)
- Run with
sudo(Linux/macOS) - Run as Administrator (Windows)
- Disable antivirus temporarily
- Check if pynput installed correctly
ImportError: No module named 'pynput'Solution:
pip install pynput psutil
# or
python -m pip install pynput psutilmacOS:
- System Preferences → Security & Privacy → Accessibility
- Add Terminal/Python to allowed list
- Restart terminal after granting permissions
Linux:
- Install X11 development packages
- Use X11 session (not Wayland)
- Check:
echo $XDG_SESSION_TYPEshould showx11
Windows:
- Run as Administrator
- Check Windows Defender isn't blocking
Cause: Too frequent analysis updates
Solution:
# Reduce analysis frequency in _analysis_loop()
time.sleep(5) # Instead of sleep(1)Cause: Data retention too long
Solution:
monitor = BehavioralMonitor(
data_retention=1800 # 30 minutes instead of 1 hour
)def _calculate_typing_metrics(self):
baseline_wpm = 60 # Change from default 40
# ... rest of calculation# More sensitive (alerts earlier)
'high_fatigue_score': fatigue_score > 60 # Instead of 70
# Less sensitive
'high_fatigue_score': fatigue_score > 80# Real-time (last 30s)
monitor = BehavioralMonitor(analysis_window=30)
# Long-term trends (last 5 minutes)
monitor = BehavioralMonitor(analysis_window=300)import json
import csv
with open('session_data.json', 'r') as f:
data = json.load(f)
with open('metrics.csv', 'w') as f:
writer = csv.writer(f)
writer.writerow(['timestamp', 'wpm', 'fatigue', 'health'])
metrics = data['current_metrics']
typing = metrics['typing_metrics']
writer.writerow([
metrics['timestamp'],
typing['wpm'],
typing['fatigue_score'],
typing['health_score']
])import requests
def check_fatigue_and_alert(monitor):
fatigue = monitor.get_fatigue_indicators()
if fatigue['overall_fatigue_level'] > 70:
requests.post('https://your-webhook.com/alert', json={
'message': 'High fatigue detected!',
'level': fatigue['overall_fatigue_level']
})| Metric | Formula | Range |
|---|---|---|
| WPM | (keystrokes / time / 5) * 60 |
0-120+ |
| Fatigue | (wpm_factor + variance + pauses) / 3 |
0-100 |
| Health | 100 - fatigue + rhythm_bonus |
0-100 |
| Rhythm | 100 - (variance / 0.5) * 100 |
0-100 |
| Smoothness | 100 - (direction_changes / moves * 100) |
0-100 |
| Active% | (active_time / total) * 100 |
0-100 |
- Calibrate baseline: Run normal work session to establish your personal WPM
- Set realistic thresholds: Adjust fatigue alerts to your patterns
- Review weekly: Export and analyze weekly trends
- Respect signals: Take breaks when fatigue detected
- Don't track sensitive apps: Add exclusion list
- Aggregate metrics: Store summaries, not raw events
- Respect privacy: Never log actual content
- **Be transpar