human-presence
Keep macOS "in use" with pointer motion that actually looks human.
Most keep-awake tools jump the cursor one pixel and call it a day. That is a teleport: no path, no acceleration, always the same direction. This one generates real trajectories instead.
human-presence -g 25 90
--------------------------------------------------------------
human-presence gap 25-90s speed x1
display 0: 1512x982 at (0,0)
display 1: 1920x1080 at (1512,-459)
Ctrl-C to stop.
--------------------------------------------------------------
10:54:52 micro 20px -> (1185,452)
10:55:33 hop 79px -> (1257,518)
10:55:33 scrolled 4 notches up
10:56:41 travel 612px -> (410,244)
What makes the motion human
| Property | How it is modelled |
|---|---|
| Path shape | Cubic Bézier with a randomised bow. ~75% single arc, ~25% lazy S. Never a straight line. |
| Speed | Minimum-jerk profile — zero velocity at both ends, so it eases in and eases out. |
| Duration | Fitts's law, MT = a + b·log2(D/W + 1), with per-move noise. 50 px ≈ 280 ms, 1200 px ≈ 730 ms. |
| Precision | Per-step tremor, plus overshoot and a corrective submovement on ~35% of long reaches. |
| Hesitation | Occasional mid-flight pause on long travels, like a person re-aiming. |
| Timing | Log-uniform gaps, so short pauses outnumber long ones. Sometimes a burst of 2–4 quick actions. |
| Target choice | ~45% micro-drift (2–25 px), ~35% short hop (60–350 px), ~20% long travel across the screen. |
| Scrolling | Bursts of 2–6 notches in one direction, each weaker than the last. |
Install
Requires macOS and Python 3.9+.
pip install human-presence
From a checkout:
git clone https://github.com/agn-7/human-presence.git
cd human-presence
pip install -e '.[dev]'
Accessibility permission
macOS will not deliver synthetic pointer events until you allow it. Open System Settings → Privacy & Security → Accessibility and enable whichever app runs the command — Terminal, iTerm, or your IDE.
Without permission the events are silently dropped. Use --verify to check: if
the idle clock does not reset to 0.0s after an action, permission is missing.
Usage
human-presence [-g MIN MAX] [--speed X] [--hours HH:MM-HH:MM] [--display N]
[--margin PX] [--no-scroll] [--keys] [--yield-seconds S]
[--dry-run] [--verify] [--seed N] [-q]
| Flag | Meaning |
|---|---|
-g, --gap MIN MAX |
Seconds of real inactivity before acting. A fresh random value in this range every time. Default 25 90. |
--speed X |
Movement speed multiplier. 0.7 is slower and calmer. Default 1.0. |
--hours HH:MM-HH:MM |
Only run inside this daily window. Overnight wrap works: 22:00-06:00. |
--display N |
Pin to one display. Default: whichever screen holds the cursor. |
--margin PX |
Pixels to keep clear of every screen edge. Default 60. |
--no-scroll |
Never scroll. |
--keys |
Also tap shift occasionally. Off by default. |
--yield-seconds S |
How long to stand down after you touch the mouse. Default 120. |
--dry-run |
Print the plan, never touch the cursor. |
--verify |
Print the system idle clock before and after each action. |
--seed N |
Fix the RNG, for reproducible tests. |
-q, --quiet |
Only log actions. |
Choosing a gap
Keep the maximum below your screen-sleep setting (System Settings → Lock Screen).
| Command | Behaviour |
|---|---|
-g 25 90 |
Good default. |
-g 60 240 |
Calmer. Use when screen sleep is 5 minutes or more. |
-g 10 30 |
Busy. Only when something needs very frequent activity. |
Preview before the real run
human-presence --dry-run -g 3 6
Nothing moves. You only see what it would do.
How idle is detected
CGEventSourceSecondsSinceLastEventType — the same clock the screensaver reads.
Keyboard activity counts, not just the mouse. So the tool stays quiet while you
are genuinely working, and only acts once you actually stop.
Because that clock is also reset by the tool's own events, a gap is measured from whichever came last: you, or it.
Staying out of the way
- You always win. Touch the mouse and it stands down for
--yield-seconds. It also aborts a move mid-flight if the cursor stops tracking the path. - No hot corners. Every point is clamped into a safe box: inset from all edges, below the menu bar, clear of the corners that trigger Mission Control.
- Multi-display aware. Bounds come from
CGGetActiveDisplayList, and the cursor position fromCGEventGetLocation, which is correct in global coordinates across stacked or offset monitor layouts.
Run it at login
The repository ships a ready template at packaging/com.agn7.human-presence.plist.
Copy it to ~/Library/LaunchAgents/, edit the paths and flags inside, then:
launchctl load ~/Library/LaunchAgents/com.agn7.human-presence.plist
launchctl list | grep human-presence # confirm it is running
launchctl unload ~/Library/LaunchAgents/com.agn7.human-presence.plist # stop
Logs go to /tmp/human-presence.log and /tmp/human-presence.err.log.
launchd does not expand ~, so use absolute paths if you change them.
Grant Accessibility permission to /usr/local/bin/python3 (or whichever
interpreter the plist points at), not to Terminal.
Development
pip install -e '.[dev]'
pytest
ruff check .
Tests cover the movement model, the safe-area maths, and the CLI. The parts that need macOS are marked and skipped elsewhere; the geometry and timing tests run anywhere.
Prior art
The idea comes from carrot69/keep-presence
(CC0). This is an independent implementation with a different movement model —
trajectories instead of a one-pixel teleport, an idle clock instead of position
comparison, and no pynput dependency.
License
MIT. The full text is in the LICENSE file of the source distribution.
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 human_presence-0.1.0.tar.gz.
File metadata
- Download URL: human_presence-0.1.0.tar.gz
- Upload date:
- Size: 16.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc7ce212abf9713dd260644578d029fba7a723041e68659f7b7a1266ff8a55e5
|
|
| MD5 |
0037e281b60e68633cd457d5305510b2
|
|
| BLAKE2b-256 |
9c4bfef1b21db09be74d27936cb73d8c275b52438e5da8b9bb533a69ec93a965
|
Provenance
The following attestation bundles were made for human_presence-0.1.0.tar.gz:
Publisher:
publish.yml on agn-7/human-presence
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
human_presence-0.1.0.tar.gz -
Subject digest:
fc7ce212abf9713dd260644578d029fba7a723041e68659f7b7a1266ff8a55e5 - Sigstore transparency entry: 2615641783
- Sigstore integration time:
-
Permalink:
agn-7/human-presence@a06b39ccac03c94ef29c49ddcccb8a119bc10656 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/agn-7
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a06b39ccac03c94ef29c49ddcccb8a119bc10656 -
Trigger Event:
release
-
Statement type:
File details
Details for the file human_presence-0.1.0-py3-none-any.whl.
File metadata
- Download URL: human_presence-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcec117b619d7ef533451bc6c03c732e6eaa03dacb3c2996cd43349bb1deb76c
|
|
| MD5 |
5c1a1b5f08d3d0330e48405e1dcaa3c4
|
|
| BLAKE2b-256 |
b3ddf022654aebd6f82c03396d6d7a91a82433961c9bfa6361ff84871b2d16b8
|
Provenance
The following attestation bundles were made for human_presence-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on agn-7/human-presence
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
human_presence-0.1.0-py3-none-any.whl -
Subject digest:
dcec117b619d7ef533451bc6c03c732e6eaa03dacb3c2996cd43349bb1deb76c - Sigstore transparency entry: 2615641820
- Sigstore integration time:
-
Permalink:
agn-7/human-presence@a06b39ccac03c94ef29c49ddcccb8a119bc10656 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/agn-7
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a06b39ccac03c94ef29c49ddcccb8a119bc10656 -
Trigger Event:
release
-
Statement type: