This is the multi-page printable view of this section. .
Posts
1 - Capslock goes native
For years Capslock was a configuration file. You installed Karabiner-Elements, imported a set of rules, and Caps Lock became Escape on a tap and a Hyper key on a hold. With Command, Option, Control and Shift it reached sixteen planes of shortcuts for the cursor, editing, windows, apps and the mouse.
That worked, and it kept two costs. Karabiner-Elements had to be installed, with its driver and its permissions, just to change one key. And the layout was a JSON file: to change a binding you edited that file, and to learn the layout you kept a picture of the keyboard open.
The new Capslock is a native macOS app. It is built for the same layout, and for removing those two costs.
The same layout, nothing to install around it
Capslock ships the complete Capslock Classic layout: 357 bindings, from
⇪ H J K L for the arrows to five extra clipboards on the number row. Every
rule of the last Karabiner version has a native counterpart, and none was
dropped. App launches, sleep, and the clipboard slots used to be shell
commands; they are now built-in actions, so the default layout runs no
scripts at all.
The app needs one permission, Accessibility. It installs no kernel extension, driver, daemon, or privileged helper. See How Capslock works for how that is possible.
An editor instead of a JSON file
The Keyboard page draws your keyboard with every action on its key. Click a
key to rebind it, disable it, put back the Classic default, or see its YAML.
A mapping can carry app rules, so ⇪ N can delete a word everywhere and send
⌃W in Terminal. Every change is applied as one transaction, and the
configuration is still a plain YAML file you can export, import, and keep in
version control.
Shell commands remain possible and need your approval before they run. A command that arrives in an imported file is switched off until you review it.
Learning built in
Hold Caps Lock and wait, and the current plane appears on screen. The Playground has short lessons that follow your own mappings, a Safe Practice mode that previews actions with side effects instead of running them, and a Free Play view that shows what the last key did without recording what you type.
Always a way back
A tool that owns your Caps Lock key has to give it back reliably. Capslock changes only the one key mapping it adds, restores it on Pause and Quit, and leaves other tools' mappings alone. A small helper restores it after a crash. If Karabiner-Elements or another tool already uses Caps Lock, Capslock does not start, and Diagnostics says what to change.
Two ways to be Hyper
The Karabiner layout made Caps Lock look like ⌃⌥⇧⌘ to every app, and many people built Alfred, Raycast or Moom shortcuts on that. Capslock keeps it as Classic mode, the default. Native mode keeps Hyper private to Capslock, for those who would rather other apps never saw it.
Where it stands
Capslock 0.4.0 is a release candidate. The download goes up once it is Developer ID signed, notarized, tested on a clean account and on Intel and macOS 14, and its update feed is verified. Until then the documentation describes the app as it will ship, and the Download page has its status.
The Karabiner-Elements configuration stays available, free, as the Karabiner Edition.
2 - How Capslock works: F18, an event tap, and a mapping it gives back
Caps Lock sits in the best spot on the keyboard and does the least useful job. Capslock gives it a new one: tap it for Escape, hold it for Hyper, and combine it with the four modifiers to reach sixteen control planes. This post explains how a native app does that on macOS without a kernel extension, a driver, or a privileged helper.
Caps Lock is not a modifier
macOS does not report Caps Lock as a key that goes down and comes back up. It reports a lock state that flips, and it adds a short delay so that brushing the key does not toggle it. That is fine for typing capitals and useless for a modifier that must be held.
So Capslock starts with a remap. At activation it asks hidutil to send the
physical Caps Lock as F18, a key that no Apple keyboard has. F18 arrives as
an ordinary key-down and key-up pair, which the app can treat as "held" and
"released".
F18 has a second advantage: it is not one of the system modifiers. Holding Caps and Shift and pressing H arrives as F18 held, Shift held, H. Nothing in macOS combines F18 with Shift into something else, so all four modifiers stack on top of Caps for free.
Reading keys from an event tap
Capslock installs a CGEventTap at the HID level, at the head of the queue, as
an active tap. Active means it can change or drop events, not just observe
them. macOS gates that on the Accessibility permission. It does not need
Input Monitoring, which only applies to listen-only taps.
If the permission is missing when the app starts, it keeps retrying. The tap installs the moment you grant access, without a relaunch. The tap runs on its own thread, and its callback does little more than compare integers, so typing does not slow down.
Resolving a chord
When F18 goes down the app records that Caps is held and swallows the event. When F18 comes back up it decides what the press was:
- A tap, with no other key pressed, runs the tap action. In the default profile that is Escape.
- A chord, Caps plus another key, runs the action mapped to that key on the current plane, and the original key never reaches the app in front.
- A double tap, when a mapping defines one, runs that instead. The single tap waits for the double-tap window so the two cannot both fire.
The plane comes from the modifiers you hold with Caps. Each modifier is a bit: Command 1, Option 2, Control 4, Shift 8. Their sum names one of sixteen planes, from 0 for Caps alone to 15 for all four. Matching is exact. A chord with an extra modifier never falls back to a less specific plane, so adding Shift to a navigation key can mean "select" without surprising you elsewhere.
Every event Capslock synthesizes carries a marker, and the tap skips events that carry it. Without that, the arrow key Capslock sends for Caps+H would come straight back through the tap.
Two ways to be Hyper
Earlier Capslock layouts implemented Hyper by holding the four right-side modifiers. Launchers and window managers such as Alfred and Moom listen for that combination, and many people have shortcuts built on it.
Capslock keeps this as a choice rather than an implementation detail:
- Classic Compatibility holds right Command, Option, Control and Shift for as long as Caps is down, so other apps see Hyper as before.
- Native Clean keeps Hyper private. Other apps see nothing while Caps is held, only the actions it produces.
In both modes the plane is computed from the modifiers you physically press. The right-side keys Capslock holds for compatibility are excluded, or every chord would land on plane 15.
Owning only what it changed
hidutil property --set replaces the whole user key mapping. The obvious
implementation writes its own list on start and clears the property on quit,
which deletes any mapping another tool or the user had set. Capslock treats the
existing mapping as somebody else's:
- It reads the current mapping and refuses to start if another entry already remaps Caps Lock.
- Before changing anything it writes a recovery journal with the previous mapping and the single entry it is about to add.
- It merges only its own entry and reads the result back to confirm it.
- On Pause or Quit it removes only that entry and restores the previous mapping, unless something else changed it in the meantime.
If another program changes the mapping while Capslock runs, Capslock stops and reports a conflict instead of fighting over the last write. It checks again after sleep and wake, when a keyboard is plugged in, and when the user session changes. This is also why Pause gives you a real Caps Lock back, not an F18 that is merely passed through.
When the app does not quit cleanly
A crash or a forced quit skips the clean-up in step 4. Two things cover that case. A small watchdog embedded in the app reads the recovery journal and watches its parent process; if the app exits without releasing the mapping, it restores the previous one. The next launch also reconciles any journal it finds before it installs the tap.
If you ever need to clear the mapping by hand, use the app's own command, which removes only the Caps Lock entry it added:
Do not reset the mapping with an empty list. That is global and would remove every other tool's remapping too. The mapping does not survive a restart in any case.
What stays local
Everything above happens on your Mac. Capslock has no analytics or advertising SDK and does not send keys, mappings, clipboard contents, or usage data anywhere. Its diagnostic log leaves out typed text, clipboard contents, Shell output, and app history. The privacy page lists what is stored and when the app uses the network.