The Zero-Reach Stack, Episode #1 - How to Ditch Your Mouse with KMonad

If you’re in tech (which you probably are, given that you’re here!), you’ve probably spent a small fortune on a chair to save your back or a monitor to save your eyes. But have you ever counted how many times a day you move your hand 15 centimeters (6 inches for my freedom-loving audience) to the right just to click a button?

It’s a micro-interruption. A tiny context switch that, over eight hours, feels like death by a thousand cuts.

In the last couple of years, I’ve been obsessed with having a ‘mouseless’ workflow. Not because I’m a masochist (though my time spent debugging KMonad configs might suggest otherwise), but because there is a specific kind of ‘flow state’ you hit when your hands never leave the home row. It started with Vim, but it quickly spiraled into an entire OS-level ecosystem.

Illustration of hands on keyboard home row position

In this series, I want to talk about how I use various tools to turn my workstation into a keyboard-driven powerhouse.

Eliminating the mouse from my workflow meant I needed to find solutions for the following problems:

  • App-level keyboard shortcuts tend to be limited in functionality and configurability
  • Keyboard shortcuts need to be more robust and capable to justify such a transition; the tool should serve me, and not the other way around
  • The modern computer relies heavily on you having a mouse, so I would need a way to move the cursor, click, and drag, as well as potentially clicking stuff without manipulating the cursor
  • Having a tool that works on both MacOS and Linux would be nice to have

With these “problems” in mind, I set forth to find solutions. In this post, we’ll focus on the first and second problems.

Fair warning: there’s a learning curve, and you will look like you’re hacking the Matrix to anyone watching over your shoulder.

If you’ve ever felt like injecting your keyboard with steroids, now’s your chance to make that dream a reality. By the end of this post, you’ll either check yourself into a mental hospital or come out on top with what looks like a keyboard but acts more like a PLC (programmable logic controller).

The software I chose for remapping my keys is KMonad, a powerful tool (perhaps too powerful) allowing you to create custom functions, workflows, and even execute shell commands, with full control of basically any pattern and outcome your heart will desire. KMonad supports MacOS, Linux, and Windows. In this post, I will be working on a MacOS machine.

KMonad uses a lisp-like configuration file, where you can configure layers, you have a base layer on which you can stack as many other layers as you’d like, and in each layer, you can configure a certain physical key or combination, to perform vastly different things.

Now, KMonad boasts a very robust set of functions you can use, each with its own distinct features, quirks, and limitations. The learning curve is indeed quite difficult, but definitely worth it. I’m not going to dive into the different functions too much, as this would make this post long and unfocused, but go ahead and read the official tutorial.

To use KMonad in its simplest form, a couple of different blocks are a must:

  • defcfg - Configuration options for KMonad
  • defsrc - A representation of the physical keyboard
  • deflayer - We can specify as many of these as we want, the first one will be the base layer

Let’s look at an example for each.

(defcfg
  ;; For MacOS
  input  (iokit-name "BT5.0 KB")
  output (kext)

  fallthrough true
  allow-cmd true
)

This defcfg example specifies which specific keyboard this configuration is for (in my case, with the identifier BT5.0 KB), the output (which in MacOS is either dext or kext, more info in the docs), and specifies whether fallthrough and allow-cmd are true. If fallthrough is enabled, an unmapped key in the current layer will fall through to the layer behind it, and emit its key. If allow-cmd is enabled, you’ll be able to execute shell commands, and it’s obviously true because I like living on the cliff of accidentally fucking up my OS or whatever.

(defsrc
  esc  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ] \
  caps a    s    d    f    g    h    j    k    l    ;    '    ret
  lsft z    x    c    v    b    n    m    ,    .    /       rsft
  lctl lmet lalt       spc                       ralt cmps rctl Fn
  rght up
)

defsrc is you telling KMonad what your keyboard physically looks like.
In this example, you can see the layout of my (61 key) keyboard.

Now that we got the source layer out of the way, we can finally get to layers and aliases.

Aliases allow you to define complex behaviors and give them a nickname. Think of it like a variable in code. Instead of writing out “a button that acts like Escape when tapped but toggles a layer when held” every time, I just define it once.

Let’s say I want to have a button which will act as the escape key on tap, but toggle a different layer when I hold it for more than 150 milliseconds (don’t get intimidated by the syntax, and don’t get too hung up on what everything things, this is just an example):

(defalias
  ;; Tap for Esc, Hold for Arrows layer
  prsarws (tap-hold-next-release 150 esc (layer-toggle arrows))
)

Once you have your aliases, you map them into a layer. The deflayer block must match the shape of your defsrc exactly. If you have 61 keys in your source, you need 61 keys in your layer.

Here’s where my home row starts to take shape. Let’s take a look at my first layer:

(deflayer layer1
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ] \
  @prsarws a s    d    f    g    h    j    k    l    ;    '      ret
  lsft z    x    c    v    b    n    m    ,    .    /    rsft
  lctl lmet lalt      spc                       ralt cmps rctl Fn
)

So what do we have here?
This layer is more or less identical to the source definition, but with a few key differences.

  • Notice that the esc key is defined here as grv, I want the default behavior of my escape key to be a backtick, and so KMonad will emit a backtick every time I press the (physical) escape key.
  • Notice that the caps lock key is set to invoke the command aliased to prsarws, which we have defined in defalias as a “command which emits escape when pressed, but toggles a layer called arrows when held.

So, where even is this arrows layer? Here it is!

(deflayer arrows
  _  _    _    _    _    _    _    _    _    _    _    _    _    _
  _  bspc    _    _    _    _    _    _ _    _    _    _    _ _
  _ ret _   _    _    _    lft    down    up    rght    _    _     _
  _ _    _    _    _    _    _    _    _    _    _    _
  _ _ _       _                        _ _ _   _
)

The underscores ( _) you see in the layer definition basically tell KMonad: ‘Don’t do anything special here, just use whatever the layer below this one is doing’ (remember the fallthrough option from defcfg?).

Given this configuration, when I hold the caps lock key, if I press h/j/k/l, it emits the corresponding arrow keypress, if I press q, for example, it emits a backspace press, and if I press a, it emits a return (or enter) keypress. pretty nifty, right?

Once you master the intricacies of KMonad configurations, you can create a bunch of really awesome stuff. Here are some examples for stuff I use kmonad for:

  • Play/pause/next/prev song on my spotify-player client (a TUI based spotify client!)
  • type out frequently used flags or words (such as -oyaml or -owide) with fewer keypresses
  • Move windows between workspaces and monitors (with Aerospace/i3window manager on which I will elaborate in a future post)
  • And many, many more…

You can see a pretty robust, accurate example of my configuration here.

All of this magic happens without my right hand ever drifting toward that lonely mouse on the edge of my desk.

KMonad is the foundation, but the journey to a zero-reach stack is just beginning. If you’re already using Karabiner or Kanata and think I’m making a mistake, let’s argue about it in the comments or on LinkedIn.

Otherwise, grab another coffee and keep your eyes peeled for Episode #2, where we’ll talk about warpd, Homerow, and SurfingKeys - the tools that finally made my mouse a desk ornament.
In the meantime, why don’t you check out my other posts?

Oh and by the way, if you’re a Hebrew speaker, you can see this talk I gave in 2022, showcasing the toolkit I used when I started this journey, some tools changed, while others stuck :)

Categories