Discreet system-tray posture monitor powered by MediaPipe and OpenCV.
Project description
BatesPosture
A real-time posture monitoring application that uses computer vision to analyse your posture through your webcam. Integrated into the system tray, it provides instant visual feedback, configurable alerts, and session analytics to help maintain proper ergonomics while working.
Features
- Real-time posture scoring (0–100) with colour-coded tray icon
- System tray integration — always visible, never in your way
- Live dashboard with sparkline history and session statistics (avg, min, max, best streak)
- Persistent dashboard history — sparkline survives closing and reopening the window
- Configurable tracking intervals (continuous or scheduled) with break reminders
- Desktop notifications with cooldown throttling and focus-mode suppression
- Onboarding wizard with 6-second calibration to capture your personal baseline
- SQLite database logging with CSV export
- Rotating log files for persistent diagnostics (
~/.batesposture_logs/app.log) - Adaptive resolution — automatically drops to 640×480 on low-end hardware when enabled
- GPU acceleration toggle (forces MediaPipe complexity-2 model)
- All processing happens locally — no video or pose data leaves your machine
Technical Stack
- MediaPipe — pose landmark detection (33 body landmarks, configurable model complexity)
- OpenCV — frame capture, CLAHE enhancement, landmark visualisation
- PyQt6 — system tray, dashboard window, settings dialog, onboarding wizard
- NumPy — vectorised posture metric computation and rolling score buffer
- SQLite3 (WAL mode) — persistent storage for scores, landmarks, and dashboard history
- psutil — hardware detection for adaptive resolution
- Python
threading.Lock— thread-safe score buffering between camera and UI threads logging.handlers.RotatingFileHandler— 5 MB / 3-backup rotating log files
Install
BatesPosture is distributed via PyPI — no unsigned binaries, no SmartScreen warnings.
pip install batesposture
batesposture
Or run without installing:
pip install batesposture
python -m batesposture
Requirements: Python 3.10+, a webcam, and Windows 10/11 or a modern Linux desktop (Ubuntu 20.04+, Fedora 35+, etc.).
Linux / GNOME: may need the AppIndicator extension for system-tray support.
Development Setup
This project uses uv for dependency management.
# Install all dependencies (including dev/test tools)
uv sync --all-groups
# Run the application
uv run python -m batesposture
# Run tests
uv run python -m pytest
Releasing a new version
- Bump the version in
pyproject.toml - Commit and push, then tag the commit:
git tag v1.0.0 git push origin v1.0.0
- GitHub Actions runs tests, builds the wheel, and publishes to PyPI automatically.
- A GitHub Release is created with install instructions.
PyPI setup: the first publish requires creating a trusted publisher on PyPI pointing to this repo with environment name
pypiand workflowbuild.yml.
Usage
- Launch the app — the tray icon appears (grey circle when idle)
- Click the tray icon → Start Tracking (or
Ctrl+Shift+T) - The icon updates in real time: red (poor) → amber (fair) → green (excellent)
- Open the dashboard (
Ctrl+Shift+D) to see live video, sparkline, and session stats - Configure alerts, intervals, and thresholds via Settings (
Ctrl+,) - Export session data to CSV via Export Data as CSV…
Configuration via Environment Variables
All settings can be overridden at startup with the prefix POSTURE_<SECTION>_<FIELD>:
# Run at 15 FPS on a slower machine
POSTURE_RUNTIME_DEFAULT_FPS=15 batesposture
# Automatically drop to 640×480 on low-end hardware
POSTURE_RUNTIME_ADAPTIVE_RESOLUTION=true batesposture
# Enable GPU-optimised MediaPipe model
POSTURE_ML_ENABLE_GPU=true batesposture
# Silence notifications
POSTURE_RUNTIME_NOTIFICATIONS_ENABLED=false batesposture
# Use a different camera
POSTURE_RUNTIME_DEFAULT_CAMERA_ID=1 batesposture
See batesposture/services/settings_service.py → KEY_TO_SECTION_FIELD for a full list of available keys.
Default Tuning Values
| Constant | Default | Description |
|---|---|---|
POOR_POSTURE_THRESHOLD_DEFAULT |
60 | Score below which a notification fires |
SCORE_THRESHOLD_DEFAULT |
65 | Score used to track good-posture streaks |
DEFAULT_POSTURE_WEIGHTS |
(0.2, 0.2, 0.15, 0.15, 0.15, 0.1, 0.05) |
Per-metric contribution to overall score |
BREAK_REMINDER_MINUTES |
50 | Minutes of continuous tracking before a break prompt |
CALIBRATION_DURATION_SECONDS |
6 | Baseline sample length during onboarding |
CALIBRATION_TIMEOUT_MARGIN_SECONDS |
6 | Extra seconds before the calibration thread is cancelled |
Privacy
All video processing runs locally. Pose landmarks and scores are only written to the local SQLite database when database logging is explicitly enabled. No data is transmitted externally.
Troubleshooting
Camera not detected
- Try a different camera index:
POSTURE_RUNTIME_DEFAULT_CAMERA_ID=1
Calibration fails during onboarding
- Ensure adequate lighting and that your head and shoulders are fully in frame
- Move closer to the camera and try again
Performance issues / lag
- Enable adaptive resolution:
POSTURE_RUNTIME_ADAPTIVE_RESOLUTION=true - Reduce frame rate:
POSTURE_RUNTIME_DEFAULT_FPS=15 - Lower model complexity in Settings → Advanced → Model complexity (0 is fastest)
GPU acceleration not working
- The
enable_gputoggle forces MediaPipe complexity-2 and relies on the device's ONNX Runtime or CUDA support. Falls back to CPU silently if unavailable.
Log files
~/.batesposture_logs/app.log(rotates at 5 MB, keeps 3 backups)
Contributing
Contributions are welcome. Please open an issue or pull request on GitHub.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file batesposture-1.1.0.tar.gz.
File metadata
- Download URL: batesposture-1.1.0.tar.gz
- Upload date:
- Size: 729.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0770cacfecb1f32c3fb70f8a3df4f41d0792a85af7618b9f60a5cc7ab5f466e3
|
|
| MD5 |
70f527a022ac63b4fa4d7ebf76c7fc7b
|
|
| BLAKE2b-256 |
0497d71b1db2ff9e8e47f08cfb6c176d2980b701f93bb509f91a808f040dcfc0
|
Provenance
The following attestation bundles were made for batesposture-1.1.0.tar.gz:
Publisher:
build.yml on wtbates99/batesposture
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
batesposture-1.1.0.tar.gz -
Subject digest:
0770cacfecb1f32c3fb70f8a3df4f41d0792a85af7618b9f60a5cc7ab5f466e3 - Sigstore transparency entry: 1262583672
- Sigstore integration time:
-
Permalink:
wtbates99/batesposture@dc82104e750ddb93c21b254a2872608b89e655dc -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/wtbates99
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yml@dc82104e750ddb93c21b254a2872608b89e655dc -
Trigger Event:
push
-
Statement type:
File details
Details for the file batesposture-1.1.0-py3-none-any.whl.
File metadata
- Download URL: batesposture-1.1.0-py3-none-any.whl
- Upload date:
- Size: 344.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a20f65e95cdb3c795be82a77026a6aa9a5e28dffd9fb8d737641873f5099de83
|
|
| MD5 |
1d21c4ae7ca4e4939e9d636d464c3347
|
|
| BLAKE2b-256 |
7f849cb4adaa1243094ab069a9bf4f681336dda05fec3e1aaf887dbbb8d436dc
|
Provenance
The following attestation bundles were made for batesposture-1.1.0-py3-none-any.whl:
Publisher:
build.yml on wtbates99/batesposture
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
batesposture-1.1.0-py3-none-any.whl -
Subject digest:
a20f65e95cdb3c795be82a77026a6aa9a5e28dffd9fb8d737641873f5099de83 - Sigstore transparency entry: 1262583711
- Sigstore integration time:
-
Permalink:
wtbates99/batesposture@dc82104e750ddb93c21b254a2872608b89e655dc -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/wtbates99
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yml@dc82104e750ddb93c21b254a2872608b89e655dc -
Trigger Event:
push
-
Statement type: