HRM — Home Row Mods for macOS
Icon from Keycap Icons by /u/aphaits
A native macOS menu bar app that turns your home row keys into dual-function keys — tap for the letter, hold for a modifier. Built on a timeless tap-hold implementation where hold vs tap is decided entirely by other key activity, not by duration timers.
When you press a home row key:
- Tap (press and release with no other keys) — types the letter normally
- Hold (press, then press and release another key) — acts as a modifier (Shift, Control, Option, or Command)
Unlike timer-based implementations, HRM uses a timeless approach. There is no hold threshold timer — the decision is based purely on whether another key was pressed and released while the home row key was down. This eliminates the latency tradeoff between fast typing and reliable modifier activation.
brew tap wontaeyang/hrm
brew install --cask hrm- Download
HRM.zipfrom the latest release - Unzip and drag
HRM.appto your Applications folder
- Launch HRM — it will appear in your menu bar
- Grant Accessibility permission when prompted (System Settings > Privacy & Security > Accessibility)
Left Hand Right Hand
┌───┐┌───┐┌───┐┌───┐┌───┐ ┌───┐┌───┐┌───┐┌───┐┌───┐
│ A ││ S ││ D ││ F ││ G │ │ H ││ J ││ K ││ L ││ ; │
│ ⌃ ││ ⌥ ││ ⌘ ││ ⇧ ││ │ │ ││ ⇧ ││ ⌘ ││ ⌥ ││ ⌃ │
└───┘└───┘└───┘└───┘└───┘ └───┘└───┘└───┘└───┘└───┘
G and H are disabled by default. Each key's modifier assignment can be changed or disabled in the key binding detail view.
Only triggers a hold when the other key is on the opposite hand. This prevents the most common misfire — fast same-hand rolls like "as" or "df" being misinterpreted as modifier combos.
- Example: Holding
A(Control) + pressingJ(right hand) =Control+J - Example: Rolling
AthenS(both left hand) = types "as"
Enabled by default. Can be toggled globally or overridden per key.
Controls when the bilateral hand check happens during a hold:
- Key Down (default) — checks the hand immediately when the other key is pressed. Same-hand keys are rejected instantly and resolve as a tap.
- Key Up — waits until the other key is released before checking. This allows combining multiple modifiers on the same hand (e.g., holding
A+Sfor Control+Option) but adds slight latency to same-hand typing.
Only applies when Bilateral Filtering is enabled.
After tapping a home row key, pressing it again within this window always types the letter — it never enters the hold state. This makes it easy to repeat characters (e.g., typing "ll" quickly).
- Default: 150ms
- Range: 0–999ms (0 disables)
If any key was pressed within this time window, the home row key immediately types the letter instead of entering the undecided state. This prevents accidental holds during fast typing bursts.
- Default: 150ms
- Range: 0–999ms (0 disables)
Each key binding can override the following global settings:
| Setting | Override Options |
|---|---|
| Bilateral Filtering | Global / On / Off |
| Quick Tap Term | Global value or custom ms |
| Require Prior Idle | Global value or custom ms |
Click any keycap in the panel to open its detail view and configure overrides.
Most keyboards (including Apple's built-in keyboards) have limits on which keys can be registered simultaneously. This is known as keyboard ghosting and it affects home row mods when combining 3 or more modifiers.
With home row mods, modifier keys are regular letter keys at the hardware level. Unlike dedicated modifier keys (which use a separate signaling path), letter keys share the keyboard's scan matrix and certain combinations can't all be detected at once.
What works:
- Any 2-modifier combo (e.g.,
D+Jfor Cmd+Shift) - Multi-modifier combos that mix hands (e.g.,
J+K+A+4for Shift+Cmd+Ctrl+4)
What may not work:
- 3+ modifiers on the same hand (e.g.,
J+K+;— the;may not register) - Certain key combinations depending on your keyboard's matrix layout
Workaround: For shortcuts that need 3+ modifiers, use keys from both hands. For example, for Cmd+Shift+Ctrl+4 (screenshot selection to clipboard), hold D+F+; or J+K+A instead of keeping all modifiers on one hand.
Keyboards with N-key rollover (NKRO), such as most mechanical keyboards, do not have this limitation.
- macOS 14.0 or later
- Accessibility permission (required to intercept keyboard events)
swift runBuilds and runs HRM directly from source. Your terminal app (e.g., Terminal, iTerm, Ghostty) must have Accessibility permission enabled in System Settings > Privacy & Security > Accessibility.
swift build -c release --arch arm64 --arch x86_64This produces a universal binary at .build/apple/Products/Release/HRM.
The scripts/release.sh script automates building, code signing, notarization, and packaging:
# Pass credentials via env vars
APPLE_ID="your@email.com" APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx" ./scripts/release.sh
# Or run interactively (will prompt for credentials)
./scripts/release.shThe script will:
- Build a universal binary (arm64 + x86_64)
- Create and sign an
HRM.appbundle with Developer ID - Submit to Apple for notarization
- Staple the notarization ticket
- Package as
HRM.zip
To publish a GitHub release:
gh release create v1.0 HRM.zip --title "HRM v1.0" --notes "Initial release"Created with Claude Code.
Built on concepts from:
- Home Row Mods — comprehensive guide to home row mod concepts and configuration
- Timeless Homerow Mods — the timeless tap-hold approach that inspired this implementation
- QMK Tap-Hold Documentation — QMK firmware tap-hold behavior reference
- ZMK Hold-Tap Behavior — ZMK firmware hold-tap documentation