indexpost archiveatom feed syndication feed icon

Colemak Attack

2018-10-27

I'm messing around with alternative keyboard layouts and it occurred to me that I should try and qualify their potential impact on my day-to-day work.

What is Colemak?

According to the website:

Colemak is a modern alternative to the qwerty and Dvorak layouts. It is designed for efficient and ergonomic touch typing in English.

In the spirit of some of my other posts, I thought it would be interesting to try and visualize the potential differences. While you can find similar tools online, I look at these exercises as a kind of kata. It's like building a library of patterns in my head so that when I see a similar problem in the future I have a point of reference.

I set about analyzing and visualizing a heatmap for the keyboard positions in both the standard qwerty layout, and compare it to Colemak. For fun, I figured I'd give it a shot in Emacs lisp.

Implementation

The first (and tedious) part is to define the x and y positions of characters on the keyboard layout, according to the SVG image above. This was done by manually checking the image in Inkscape.

(setq *COLEMAK-XY-MAPPINGS*
      '(("`" 30 270)
        ("1" 90 270)
        ("2" 150 270)
        ("3" 210 270)
        ("4" 270 270)
        ("5" 330 270)
        ("6" 390 270)
        ("7" 450 270)
        ("8" 510 270)
        ("9" 570 270)
        ("0" 630 270)
        ("-" 690 270)
        ("=" 750 270)
        ("Q" 120 210)
        ("W" 180 210)
        ("F" 240 210)
        ("P" 300 210)
        ("G" 360 210)
        ("J" 420 210)
        ("L" 480 210)
        ("U" 540 210)
        ("Y" 600 210)
        (";" 660 210)
        ("[" 720 210)
        ("]" 780 210)
        ("\\" 840 210)
        ("A" 135 150)
        ("R" 195 150)
        ("S" 255 150)
        ("T" 315 150)
        ("D" 375 150)
        ("H" 435 150)
        ("N" 495 150)
        ("E" 555 150)
        ("I" 615 150)
        ("O" 675 150)
        ("'" 735 150)
        ("Z" 165 90)
        ("X" 225 90)
        ("C" 285 90)
        ("V" 345 90)
        ("B" 405 90)
        ("K" 465 90)
        ("M" 525 90)
        ("," 585 90)
        ("." 645 90)
        ("/" 705 90)))

From there, I've made a hash table to track the character counts along with a helper function that consumes a list of the type in the above mapping and adds a 0 for the initial value to the tail of the list.

(setq *KB-CHARACTER-COUNTS* (make-hash-table))

(defun make-counter-hash (param-list hash-table)
  (puthash (string-to-char (first param-list))
           (nreverse (cons 0 (nreverse (rest param-list))))
           hash-table))

Due to the format of the value-side of this hash table (("C" 285 90 0)), I also need a function to increment a given key. It does this by first picking out the value for a given key and then incrementing the last list element:

(defun increment-counter (c counter-hash-table)
  (if (gethash c counter-hash-table)
      (incf (caddr (gethash c counter-hash-table)))))

The last piece to construct the hash table is to insert all of the keys, it accomplishes as much by invoking mapcar over a partially-applied anonymous function, where the partial is the position mappings list:

(mapcar (lambda (l) (make-counter-hash l *KB-CHARACTER-COUNTS*))
        *COLEMAK-XY-MAPPINGS*)

With that amount of setup done, I can simply apply the incrementer over a body of text. For a relevant corpus of text, I've concatenated together all of the posts to-date in this blog. Emacs can take, as a string, the entire contents of an open buffer:

(with-current-buffer "all-posts.txt"
  (cl-loop for c across (upcase (buffer-string))
           do (increment-counter c *KB-CHARACTER-COUNTS*)))

The result is an emacs buffer (named *HEATMAP-DATA*) that looks like this (albeit longer):

30 270 1579
90 270 1156
150 270 814
210 270 495
270 270 442
330 270 406
390 270 321
...

Visualizing It

Back to gnuplot for this one, although the results have been harder to come by than I anticipated. While there is some support for heatmaps in gnuplot, this particular case proved annoying to get to what I consider satisfactory.

unset xtics
unset ytics
unset colorbox

set terminal svg size 900,300
set output "colemak.svg"

# treat the data as x, y, z positions
set dgrid3d
# pm3d refers to palette-mapped, to give us a color-based z-scale
set pm3d map interpolate 1,1

splot "colemak.dat"

The layout looks like this (courtesy of wikimedia, CC BY-SA 3.0):

Colemak keyboard layout

And the usage heatmap for all of the posts on this blog, were I to have used it:

Colemak keyboard heatmap

qwerty Comparison

Using the same technique, but substituting those x and y values for the keys that are different results in the following graphic for keyboard usage under qwerty (also from wikimedia):

qwerty keyboard layout

qwerty keyboard heatmp

Thoughts

The end result isn't perfect, I would have preferred to have either overlaid the keyboard image itself onto the heatmap, or have more precise key-tracking than my (admittedly basic) center of key position with interpolation.

As to the keyboard layout, I haven't entirely switched over but I do think I'll give it a shot, the image demonstrates pretty well just how home-row focused Colemak ends up being. I'm not pretty well aware of how many "reaches" I make in my day to day typing under qwerty.