Skip to main content

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 from CGEventGetLocation, 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

human_presence-0.1.0.tar.gz (16.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

human_presence-0.1.0-py3-none-any.whl (13.6 kB view details)

Uploaded Python 3

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

Hashes for human_presence-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fc7ce212abf9713dd260644578d029fba7a723041e68659f7b7a1266ff8a55e5
MD5 0037e281b60e68633cd457d5305510b2
BLAKE2b-256 9c4bfef1b21db09be74d27936cb73d8c275b52438e5da8b9bb533a69ec93a965

See more details on using hashes here.

Provenance

The following attestation bundles were made for human_presence-0.1.0.tar.gz:

Publisher: publish.yml on agn-7/human-presence

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for human_presence-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dcec117b619d7ef533451bc6c03c732e6eaa03dacb3c2996cd43349bb1deb76c
MD5 5c1a1b5f08d3d0330e48405e1dcaa3c4
BLAKE2b-256 b3ddf022654aebd6f82c03396d6d7a91a82433961c9bfa6361ff84871b2d16b8

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page