Skip to main content

The Jiggler

Here's the actual thing.

Open ~/.hammerspoon/init.lua and paste in the following. If you already have things in your config, add this below whatever's already there.

-- ── Mouse Jiggler ─────────────────────────────────────────────────────────────
-- Moves the cursor a few pixels every 45-90 seconds to prevent
-- the display from sleeping. Returns the cursor to its original
-- position 0.5 seconds later so you won't notice it.

local jigglerTimer = nil
local jiggling = false

local function scheduleJiggle()
if not jiggling then return end
local delay = math.random(45, 90)
jigglerTimer = hs.timer.doAfter(delay, function()
if not jiggling then return end
local pos = hs.mouse.absolutePosition()
local dx = math.random(2, 8) * (math.random(2) == 1 and 1 or -1)
local dy = math.random(2, 8) * (math.random(2) == 1 and 1 or -1)
hs.eventtap.event.newMouseEvent(
hs.eventtap.event.types.mouseMoved,
{x = pos.x + dx, y = pos.y + dy}
):post()
hs.timer.doAfter(0.5, function()
hs.eventtap.event.newMouseEvent(
hs.eventtap.event.types.mouseMoved,
pos
):post()
scheduleJiggle()
end)
end)
end

local function startJiggler()
jiggling = true
if jigglerTimer then
jigglerTimer:stop()
jigglerTimer = nil
end
hs.caffeinate.set("displayIdle", true)
scheduleJiggle()
end

local function stopJiggler()
jiggling = false
if jigglerTimer then
jigglerTimer:stop()
jigglerTimer = nil
end
hs.caffeinate.set("displayIdle", false)
end

Save the file and reload Hammerspoon. Nothing visible will happen yet — the jiggler is defined but not started. We'll wire it up to a menubar button on the next page.

If you just want to test it immediately, add these two lines temporarily at the bottom and reload:

startJiggler()
hs.alert.show("Jiggler started", 2)

Move your mouse somewhere, leave it, and watch. After 45–90 seconds the cursor will twitch by a pixel or two and return. That's it working. Remove those two test lines before continuing.

note

The hs.caffeinate.set("displayIdle", true) call tells macOS not to sleep the display due to inactivity. The mouse movement is a belt-and-suspenders approach — the caffeinate call handles the display, the jiggle handles anything that watches for actual input events (some apps and remote monitoring tools track mouse movement specifically).