Pathos is a Chrome extension that screenshots the visible browser tab every few seconds, sends it to a Flask backend that runs facial emotion classification with the DeepFace library, and draws a color-coded box with the predicted emotion over each detected face. It was built at Hack NYU 2025 as a demo of real-time emotion recognition on whatever is on screen, such as a video call or a video.
The extension talks to a hosted backend on Hugging Face Spaces, so you only need to load it:
- Clone the repo:
git clone https://github.com/aprameyak/Pathos - Open
chrome://extensions/, turn on Developer mode, click "Load unpacked", and select thefrontend/folder. - Open a normal web page with faces on it (not a
chrome://page), click the Pathos icon, then click "Start Detection".
The Start button stays disabled while the backend health check fails. If the Space is asleep, wait a minute and reopen the popup.
In the browser, each detected face gets a box colored by emotion (green happy, blue sad, red angry, purple fear, orange surprise, brown disgust, gray neutral) with a label such as HAPPY (98%). Click "Stop Detection" to remove the overlay.
The backend can also be called directly. /analyze_screen takes a base64 image (a data URL prefix is optional):
curl -X POST https://aprameyak-pathos.hf.space/analyze_screen \
-H "Content-Type: application/json" \
-d "{\"frame\": \"$(base64 < face.jpg | tr -d '\n')\"}"Real response for a 400x400 portrait of a smiling man, scores rounded:
[
{
"dominant_emotion": "happy",
"emotion_scores": {
"angry": 0.0, "disgust": 0.0, "fear": 0.0002, "happy": 98.46,
"neutral": 1.25, "sad": 0.0003, "surprise": 0.29
},
"region": { "x": 85, "y": 24, "w": 207, "h": 207 }
}
]Scores are percentages from DeepFace and region is in pixels of the (possibly resized) input image. Other endpoints: GET /health, GET /config, and GET /.
- Start and Stop buttons in the extension popup, plus a backend status indicator that polls
/healthevery 10 seconds. - Captures the visible tab as a JPEG with
chrome.tabs.captureVisibleTababout every 3 seconds while detection is running. - Classifies each face into one of seven emotions: happy, sad, angry, fear, surprise, disgust, neutral.
- Backend limits results to 10 faces, downsizes images to a max side of 1024 px, and keeps an in-memory cache of the last 100 results.
- Backend: Python 3.9, Flask, Flask-CORS, DeepFace 0.0.79 (TensorFlow 2.13), OpenCV (
opencv-python-headless) - Extension: Chrome Manifest V3, plain JavaScript and HTML (no framework or build step)
- Deployment: Docker image on Hugging Face Spaces (
https://aprameyak-pathos.hf.space)
Needs Python 3.9 or Docker. DeepFace downloads its model weights on first use, so the first request is slow and needs internet access. Optional environment variables: PORT (default 7860) and DEBUG (true enables Flask debug mode).
cd backend
pip install -r requirements.txt
python app.pyOr with Docker, from backend/:
docker build -t pathos-backend . && docker run -p 7860:7860 pathos-backendCheck it with curl http://localhost:7860/health. The backend URL is hardcoded in frontend/content.js (BACKEND_URL) and frontend/popup.js (the /health check). To use your local backend, change both to http://localhost:7860 and reload the extension.
There are no automated tests. frontend/test.html and frontend/test-debug.html are manual test pages with sample face photos (loaded from Unsplash). content.js also exposes window.testPathosOverlay(), which draws fake results to check the overlay without the backend.
frontend/popup.jschecks the backend's/healthendpoint and sendsstartDetectionto the content script in the active tab, injectingcontent.jsif needed.frontend/content.jsasks the service worker (frontend/background.js) for a screenshot about every 3 seconds. The service worker callschrome.tabs.captureVisibleTaband returns a JPEG data URL.content.jsPOSTs{ "frame": <data URL> }to/analyze_screen, retrying up to 3 times with backoff.backend/app.pydecodes the image with OpenCV, resizes it if needed, and callsDeepFace.analyze(frame, actions=['emotion'], detector_backend='opencv', enforce_detection=False). Face detection uses OpenCV's Haar cascade detector and the emotion label comes from DeepFace's built-in pretrained facial expression model. No custom model was trained for this project.- The backend returns a JSON list of
{ dominant_emotion, emotion_scores, region }per face, andcontent.jsdraws the boxes in a fixed, click-through overlay.
Known limitations:
- Because
enforce_detection=False, a frame with no detected face can still return one result covering the whole image. - Box positions use the screenshot's pixel coordinates directly. They do not account for device pixel ratio or the backend's resizing, so boxes can be misaligned on high-DPI screens or large windows.
- The result cache is keyed on a hash of the first 1000 characters of the base64 frame, so very similar frames can return a cached result.
- The
descriptioninfrontend/manifest.jsonsays "No backend required", which is out of date. The extension needs the backend.
Images are decoded in memory and the backend does not write them to disk.
Pathos/
├── backend/
│ ├── app.py Flask API: /, /health, /config, /analyze_screen
│ ├── requirements.txt
│ ├── Dockerfile python:3.9-slim image, port 7860
│ └── info.MD
├── frontend/ Chrome extension (Manifest V3)
│ ├── manifest.json
│ ├── background.js service worker, captures screenshots
│ ├── content.js capture loop, backend calls, overlay
│ ├── popup.html
│ ├── popup.js start/stop controls, backend status
│ ├── test.html manual test page
│ ├── test-debug.html manual debug page
│ └── info.MD
└── LICENSE
Issues and pull requests are welcome. Please keep changes to the backend response format (dominant_emotion, emotion_scores, region) in sync with frontend/content.js.
MIT, see LICENSE.