← all apps

HAMMERSPOON_LUA_RECORDER

The macOS port. Same recorder, same workflow, written in Lua for Hammerspoon. Everything records itself, with undo, redo, and a teaching line that shows you the Lua it wrote.

runs onMactechnologyLua, Hammerspoon, Hammerspoonrepositoryhttps://github.com/markoboskoauroville/HAMMERSPOON_LUA_RECORDERlast push2026-08-08
GitHub
# Hammerspoon Lua Recorder

A keyboard and mouse macro recorder for macOS, written in Lua for [Hammerspoon](https://www.hammerspoon.org). Free, MIT licensed.

This is the macOS port of [MARCO_MACRO_RECORDER](https://github.com/markoboskoauroville/MARCO_MACRO_RECORDER), which does the same job on Windows in AutoHotkey. Same model, same workflow, same menu, same two modes. Learn one and you know the other.

**Nothing here is compiled.** There is no Xcode, no build step, no signing, no App Store. Hammerspoon reads Lua as it is. Download, run one script, tick two boxes in System Settings, done.

## What makes it different

Most recorders capture your timing along with your actions, which means a macro that played back correctly on a fast morning fails on a slow afternoon. This one throws your timing away and writes a fixed `wait(333)` between every step. Predictable beats faithful.

Everything records itself. Press a key and it is on disk before you have let go of it. There is no save key and no pause.

The recorded file is the only source of truth. Nothing is held in memory. Open `CapturedMacro.lua` in an editor mid session, change it by hand, and keep recording on top of your changes. The recorder only ever writes between two marker comments, so everything outside them is yours.

And it teaches. The big line in the status strip shows what you pressed, a pipe, and the exact Lua that was written:

```
escape                 |  hs.eventtap.keyStroke({}, "escape")
shift+cmd+s            |  hs.eventtap.keyStroke({ "shift", "cmd" }, "s")
button4 at 2456, 812   |  hs.eventtap.otherClick({x = 2456, y = 812}, 0, 4)
```

The same label goes into the macro file as a trailing comment, so the file reads like a lesson.

## Installing

You need Hammerspoon first:

```
brew install --cask hammerspoon
```

or download it from [hammerspoon.org](https://www.hammerspoon.org) and drag it to Applications. Then:

```
bash install_marco_recorder_v1.sh
```

That writes `~/.hammerspoon/marco_recorder.lua` and adds one line to your `init.lua`. It backs up anything it replaces and it is safe to run twice.

Then two boxes in **System Settings, Privacy and Security**:

- **Accessibility**, tick Hammerspoon. Without this it cannot see your keys.
- **Input Monitoring**, tick Hammerspoon as well.

Finally, click the Hammerspoon icon in the menu bar and choose Reload Config. The status strip appears at the bottom of your left screen.

To remove it: `bash uninstall_marco_recorder.sh`. Your recorded macros and your archive are left alone.

### If you would rather do it by hand

Copy `marco_recorder.lua` into `~/.hammerspoon/` and add to `init.lua`:

```lua
local marco = require("marco_recorder")
marco.start()
```

## Keys

| Key | Effect |
|---|---|
| `ctrl` or `alt` or `cmd+alt` plus `,` or `.` | The menu. Six bindings, all equivalent |
| your own shortcut | Runs the macro, in TEST mode only |
| everything else | Recorded, in RECORD mode |

macOS has no AltGr, so `cmd+alt` stands in for it and the six bindings match the Windows edition one for one.

## Two modes

**RECORD mode** records everything, always. **TEST mode** records nothing and runs what you built.

Open the menu and choose Testing mode. It asks you to press the shortcut you want. Press any combination and it becomes the trigger: it is written into `CapturedMacro.lua`, the file is loaded, and you are in TEST mode. Escape on its own cancels. The choice is remembered in `hs.settings` between restarts.

Open the menu again in TEST mode and it offers Back to Macro Recorder mode.

## Undo and redo

In the menu. Undo cuts the last step and the `wait` beneath it out of the file and remembers them, redo writes them back, and recording anything new clears the redo stack.

## The mouse

Clicks record themselves. All buttons are captured, including the extra thumb buttons, which arrive as button numbers 3 and 4.

Recording a position without clicking is solved by freezing. The menu opens from the keyboard, so at that instant the pointer has not moved. The menu grabs the coordinates right then and shows them in each row. The four mouse entries record that frozen position, not where the pointer ended up.

Coordinates are the global screen space that spans every display, so a click recorded on the second monitor plays back on the second monitor.

## Playback does not freeze your Mac

This is the one place the port improves on the original. A blocking sleep in Hammerspoon stalls the whole run loop, so a thirty step macro would beachball your Mac for ten seconds. Instead the generated file's `wait` yields inside a coroutine and comes back on a timer:

```lua
local function wait(ms)
    local co = coroutine.running()
    if co and coroutine.isyieldable() then
        hs.timer.doAfter(ms / 1000, function() coroutine.resume(co) end)
        coroutine.yield()
    else
        hs.timer.usleep(ms * 1000)
    end
end
```

The file still reads as a plain linear sequence, and it still works if you load it on its own outside the recorder.

## What a recorded file looks like

```lua
local M = {}

M.hotkey = { mods = { "ctrl" }, key = "f5" }
M.label  = "ctrl+f5"

function M.run()
    -- >>> RECORDED STEPS BELOW, the recorder writes here
    hs.eventtap.keyStroke({}, "escape")          -- escape
    wait(333)
    hs.eventtap.leftClick({x = 2456, y = 812})   -- left click at 2456, 812
    wait(333)
    -- <<< RECORDED STEPS ABOVE, the recorder writes here
end

return M
```

## Tests

There is a test suite that stubs the Hammerspoon API and drives the real module, so the file handling can be checked on any machine with Lua, not just a Mac:

```
lua5.4 test/test_recorder.lua
```

Twenty seven assertions covering recording, undo, redo, hand edits surviving, the generated file parsing and running, the hotkey rewrite, and archiving.

## Where things live

`~/.hammerspoon/CapturedMacro.lua`, the macro. `~/.hammerspoon/macro_archive/`, anything you archived. The testing shortcut lives in `hs.settings`.

## Licence

MIT. Use it, change it, ship it, sell it. Attribution appreciated, not required.

Marko Boško, Mantra Productions.