Screen change monitor built for Hyprland on Arch Linux. Detects when something changes on your screen and sends a desktop notification — ideal for waiting on long-running processes.
sudo pacman -S python-pillow python-numpy grim libnotify fzf slurp gtk4-layer-shell python-gobjectFor OCR mode (--ocr-find), also install:
sudo pacman -S tesseract tesseract-data-eng python-pytesseractFor other languages replace tesseract-data-eng with the appropriate package (e.g. tesseract-data-spa for Spanish) and pass the matching code via --ocr-lang. Available languages and their codes are listed in the Tesseract documentation.
Run without --monitor to get an interactive picker:
python hyprwatch.pyOr pass a monitor name directly to skip the picker:
python hyprwatch.py --monitor DP-1To watch a specific area, use --area without a value to draw the selection interactively with slurp:
python hyprwatch.py --areaOr pass the geometry directly:
python hyprwatch.py --area "100,200 800x600"When using --area, a red dashed border is drawn over the selected region for the duration of the session. The overlay is fully click-through — you can interact with anything inside the area normally.
| Argument | Default | Description |
|---|---|---|
--monitor |
(interactive picker) | Monitor name — omit to select from a list |
--area |
(none) | Area to capture as x,y wxh — omit value to draw interactively with slurp. Takes priority over --monitor |
--interval |
5.0 |
Seconds between checks |
--threshold |
2.0 |
Percentage change to trigger an alert |
--noise |
5 |
Per-pixel difference to ignore (reduces false positives) |
--on-change |
(notify-send) | Command to run when a change is detected |
--max-alerts |
1 |
Max alerts before stopping, 0 for unlimited |
--cooldown |
30 |
Seconds to wait after an alert before resuming |
--on-stable |
(notify-send) | Enable stable mode — optionally pass a command, omit for notify-send |
--stable-interval |
5.0 |
Seconds without change required to consider stable |
--stable-threshold |
0.05 |
Max % change to consider stable — tolerates cursor blink, use 0.0 for pixel-perfect |
--stable-noise |
0 |
Per-pixel difference to ignore in stable mode — 0 means pixel-perfect |
--show-diff |
false |
Open a diff image highlighting changed pixels when a change is detected |
--quiet |
false |
Suppress all output, only warnings and errors are shown |
--ocr-find TEXT |
(none) | Enable OCR mode — alert when TEXT is found on screen |
--ocr-lang |
eng |
Tesseract language code (requires the matching tesseract-data-* package) |
--ocr-scale |
2 |
Scale factor applied before OCR — increase if small text is not being detected. On a full monitor keep it at 2 or 3; on a small area you have more room to experiment. It is recommended to test with a few runs first to find the value where your target text is reliably recognized |
Basic usage:
python hyprwatch.py --monitor DP-1Run a custom command on change:
python hyprwatch.py --monitor DP-1 --on-change "paplay /usr/share/sounds/bell.wav"Send up to 5 alerts with a 60s cooldown between them:
python hyprwatch.py --monitor DP-1 --max-alerts 5 --cooldown 60Alert when the screen has been stable for 5 seconds (e.g. waiting for AI output to finish):
python hyprwatch.py --monitor DP-1 --on-stable "notify-send hyprwatch 'Done, check your screen'"Override stable sensitivity for a very subtle indicator:
python hyprwatch.py --monitor DP-1 --on-stable "notify-send hyprwatch done" --stable-interval 3 --stable-threshold 0.1 --stable-noise 2Open a diff image highlighting exactly what changed — works best with --area since a full monitor will always have minor pixel movement (cursor, animations, UI elements):
python hyprwatch.py --show-diff --area --noise 20The diff image is saved to /tmp/hyprwatch/hyprwatch_diff.png and opened with your default image viewer. Changed pixels are highlighted in red over the original frame — useful for spotting subtle UI changes like a status indicator flipping or a button appearing.
Wait until a specific text appears on screen — useful for GUI apps, renderers, or any process whose output you can only see visually:
python hyprwatch.py --ocr-find "Render complete" --monitor DP-1
python hyprwatch.py --ocr-find "Build successful" --area --interval 2
python hyprwatch.py --ocr-find "Exportación terminada" --monitor DP-1 --ocr-lang spa # text on screen is in SpanishAll standard options work in OCR mode (--interval, --max-alerts, --cooldown, --on-change, --area, --monitor). Since OCR itself takes 300–800ms per capture depending on the area size, very low --interval values have diminishing returns — the default of 5s is a good starting point.
Not for log files. If the output is a text file or a terminal you control,
grep,tail -f, or a shell one-liner will always be faster and more reliable. OCR mode is for when there is no other way to read the output — a GUI app, a remote desktop, a web dashboard, etc.
The overlay style is controlled by three constants at the top of hyprwatch.py:
OVERLAY_COLOR = (1, 0.15, 0.15, 0.95) # RGBA, values 0-1
OVERLAY_BORDER = 3 # border width in px
OVERLAY_RADIUS = 10 # corner radius in pxThe dash pattern ([12, 6] — 12px stroke, 6px gap) is in the draw function inside show_area_overlay.
If you run hyprwatch.py without --monitor, an interactive fzf menu will show all available monitors with their model names — just select one and press Enter.
To list monitors manually:
hyprctl monitorsLooking for the older version with generic Wayland compositor support? Check out the
v1.0-wayland-generictag.