One of the coolest aspects of building a custom keyboard is the level of customizability—not just in hardware, but also in software. My custom keyboard features QMK/VIA support, which allows me to configure any key to perform exactly as I want.
One of my favorite customizations is repurposing the Caps Lock key to function as Escape. The Caps Lock key is conveniently located, but I rarely use it, so I switched it to Escape. However, that still didn’t feel quite useful enough. I also configured my Caps Escape key to toggle a layer when held, mapping “hjkl” to the Vim arrow keys. It was a game changer.
I quickly developed muscle memory for this setup, and whenever I used my MacBook away from my desk, I found myself missing it! Recently, I discovered that I could achieve a similar functionality on my MacBook keyboard using Hammerspoon.
First, install Hammerspoon. Then, add the following to ~/.hammerspoon/CapsEscArrows.lua
:
local function pressFn(mods, key)
if key == nil then
key = mods
mods = {}
end
return function() hs.eventtap.keyStroke(mods, key, 1000) end
end
-- Create event tap to monitor modifier key changes
local escHeld = false
local keyMap = {
h = 'left',
j = 'down',
k = 'up',
l = 'right',
d = 'pagedown',
u = 'pageup'
}
-- Create event tap for key down events
local keyDownTap = hs.eventtap.new({ hs.eventtap.event.types.keyDown }, function(event)
if not escHeld then return end
local keyChar = hs.keycodes.map[event:getKeyCode()]
local flags = event:getFlags()
if keyMap[keyChar] then
local mods = {}
if flags.cmd then table.insert(mods, 'cmd') end
if flags.alt then table.insert(mods, 'alt') end
if flags.shift then table.insert(mods, 'shift') end
pressFn(mods, keyMap[keyChar])()
return true -- Prevent original key event
end
end)
-- Create flags changed event tap
local flagsTap = hs.eventtap.new({ hs.eventtap.event.types.flagsChanged }, function(event)
local flags = event:getFlags()
-- Check if escape key is pressed or released
if flags.fn then
if not escHeld then
escHeld = true
keyDownTap:start()
end
else
if escHeld then
escHeld = false
keyDownTap:stop()
end
end
end)
-- Start the flags event tap
flagsTap:start()
Next, require this file in Hammerspoon’s init.lua
:
local CapsEscArrows = require('CapsEscArrows')
Next, configure your modifier keys in “System Settings > Keyboard > Keyboard Shortcuts > Modifier Keys”, and change the “Caps Lock” action to “Escape”.
Finally, don’t forget to reload your Hammerspoon config.
Now when you hold Caps Lock / Esc, h
,j
,k
, and l
become vim arrow keys. Also as a bonus d
becomes pg down, and u
becomes pg up.
I hope this makes you as happy as it made me!