Skip to content

Commit 17fe8a5

Browse files
committed
Post on Home Row Mods
1 parent a994197 commit 17fe8a5

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
layout: post
3+
title: "Configuring Home Row Mods with Karabiner-Elements"
4+
date: 2025-05-12 02:00:00
5+
tags: keyboard
6+
---
7+
8+
This note shows how I’ve configured
9+
[home row mods (HRM)](https://precondition.github.io/home-row-mods) with
10+
[Karabiner-Elements (KE)](https://karabiner-elements.pqrs.org/).
11+
I developed this configuration to address shortcomings solutions I’ve found
12+
on Internet, e.g., this configuration never loses key presses.
13+
It turns out, it is suprisingly non-trivial to implement such a popular mod
14+
as HRM.
15+
16+
## Configuration
17+
18+
Here’s a snippet of the approach for configuring “a” as
19+
Ctrl and “s” as Command:
20+
21+
```json5
22+
{
23+
"description": "Home row mods (as) - control, command",
24+
"manipulators": [
25+
{
26+
// If I press "a" and "s"…
27+
"from": {
28+
// With any modifier…
29+
"modifiers": { "optional": ["any"] },
30+
"simultaneous": [{ "key_code": "a" }, { "key_code": "s" }],
31+
"simultaneous_options": { "key_down_order": "strict" }
32+
},
33+
// … and press something else quickly, then just output "a" and "s".
34+
"to_delayed_action": {
35+
"to_if_canceled": [{ "key_code": "a" }, { "key_code": "s" }],
36+
"to_if_invoked": [{ "key_code": "vk_none" }]
37+
},
38+
// … and release, then just output "a" and "s".
39+
"to_if_alone": [
40+
{
41+
// Do not run the delayed action. We are committed.
42+
"halt": true,
43+
"key_code": "a"
44+
},
45+
{ "key_code": "s" }
46+
],
47+
// … and hold, then just treat it as a modifier.
48+
"to_if_held_down": [
49+
{
50+
// Do not run the delayed action. We are committed.
51+
"halt": true,
52+
"key_code": "left_control",
53+
"modifiers": ["left_command"]
54+
}
55+
],
56+
"type": "basic"
57+
},
58+
// Cover the case of pressing `sa`.
59+
{
60+
"from": {
61+
"modifiers": { "optional": ["any"] },
62+
"simultaneous": [{ "key_code": "s" }, { "key_code": "a" }],
63+
"simultaneous_options": { "key_down_order": "strict" }
64+
},
65+
"to_delayed_action": {
66+
"to_if_canceled": [{ "key_code": "s" }, { "key_code": "a" }],
67+
"to_if_invoked": [{ "key_code": "vk_none" }]
68+
},
69+
"to_if_alone": [
70+
{
71+
"halt": true,
72+
"key_code": "s"
73+
},
74+
{ "key_code": "a" }
75+
],
76+
"to_if_held_down": [
77+
{
78+
"halt": true,
79+
"key_code": "left_control",
80+
"modifiers": ["left_command"]
81+
}
82+
],
83+
"type": "basic"
84+
},
85+
{
86+
"from": {
87+
"key_code": "a",
88+
"modifiers": { "optional": ["any"] }
89+
},
90+
"to_if_alone": [
91+
{
92+
"halt": true,
93+
"key_code": "a"
94+
}
95+
],
96+
"to_if_held_down": [
97+
{
98+
"halt": true,
99+
"key_code": "left_control"
100+
}
101+
],
102+
// If another key is pressed, while doing a combo with "a", just output
103+
// "a".
104+
"to_delayed_action": {
105+
"to_if_canceled": [{ "key_code": "a" }],
106+
"to_if_invoked": [{ "key_code": "vk_none" }]
107+
},
108+
"type": "basic"
109+
},
110+
{
111+
"from": {
112+
"key_code": "s",
113+
"modifiers": { "optional": ["any"] }
114+
},
115+
"to_if_alone": [
116+
{
117+
"halt": true,
118+
"key_code": "s"
119+
}
120+
],
121+
"to_if_held_down": [
122+
{
123+
"halt": true,
124+
"key_code": "left_command"
125+
}
126+
],
127+
"to_delayed_action": {
128+
"to_if_canceled": [{ "key_code": "s" }],
129+
"to_if_invoked": [{ "key_code": "vk_none" }]
130+
},
131+
"type": "basic"
132+
}
133+
]
134+
}
135+
```
136+
137+
I use this config with the following parameters:
138+
139+
- `to_if_alone_timeout_milliseconds`: 400
140+
- `to_if_held_down_threshold_milliseconds`: 110
141+
- `simultaneous_threshold_milliseconds`: 90
142+
143+
## Why so complex?
144+
145+
One reason why this configuration is so complex is that KE doesn’t let us define
146+
HRM in the form of: “if ‘a’ is held, then it’s left control”.
147+
The biggest obstacle is that if you have a manipulator for “a”, then
148+
pressing “a,” and then “s” before the `if_held` action triggers, the
149+
a-manipulator cancels, and we get nothing.
150+
We need to work with manipulators for simultaneous key presses, and we need to
151+
define them for every combination that we might encounter.
152+
We also need to work with delayed actions to cover the case when a non-home row
153+
key gets pressed while a home row key is held.

0 commit comments

Comments
 (0)