← More about keyboards

Overview

It’s pretty common in a QMK layout to have a “symbol” layer, for all those symbols that didn’t fit on the base layer, especially with smaller boards.

A symbol layer.

A lot of effort has gone into optimizing the layout of the alpha keys—Dvorak, Colemak, MTGAP, BEAKL, RSTHD, to name a few (see A guide to alt keyboard layouts). Yet I found relatively little written about how to design a symbol layer. To do something about that, this page discusses some design principles and observations about symbol layers.

Design principles

Symbols are not like alpha characters: they occur less often, and they tend to occur in isolation or in short bigrams (in C code, things like # and !=). We can learn nevertheless from work on optimizing alpha key layouts and apply some of those findings to symbol layouts:

It’s a good idea to…

And a practicality:

While each principle above is reasonable for an isolated key, they will easily conflict when getting into the design. Obviously, not all keys can be on the home row and not all bigrams can be inward rolls. We need to make compromises.

Automatic optimization?

There are keyboard layout optimizer tools (like Carpalx, xsznix/keygen, semilin/genkey, and O-X-E-Y/oxeylyzer) that automatically search for a layout that balances many above such considerations, and these could be applied to design a symbol layer. However, automatically designed layouts are notorious for having random-looking hard-to-learn arrangements. It’s also hard with these tools to make a minor adjustment to one key without reshuffling everything else. I think it’s better for these reasons to arrange the symbol keys manually.

Some existing designs

Looking at keymaps in the QMK repo, it is easy to find many existing designs for symbol layers. Here are a few for inspiration:

A reasonable default

This is the symbol layer in the default keymap for the ZSA Moonlander, the Dactyl boards, and probably many others:

A reasonable default symbol layer.

(index fingers rest on ) and 4)

The layer is easily learnable. Brackets are neatly organized on the left hand, and the leftmost two columns are simply the first six number row symbols in usual order (! @ # $ % ^). The layer manages to squeeze a numpad onto the right hand. While it’s a well-designed layer, there is room for improvement, especially for typing common bigrams.

Extend2

Another good general-purpose symbol layer design is Extend2 of DreymaR’s Extend Layer, containing some useful symbols, numpad, navigation keys, and hotkeys.

Extend2 layer.

Seniply

SteveP’s Seniply is a 34-key keymap that packs a lot of symbols in one layer, in addition to mods on the left-hand home row:

Seniply symbol layer.

(index fingers rest on Ctrl and ()

Sunaku’s symbol layer

Sunaku described this as “the crown jewel of my keyboard’s configuration,” resulting from “several hundreds of layout iterations over the last 9 years.” The layer is optimized for programming in Vim.

Sunaku’s symbol layer.

(index finger rests on $)

Many common syntax bigrams are inward rolls, including (), [], ->, <-, !=, <=, ~/. Keys relating to Vim navigation are grouped in pairs, like ^ $ (start/end of current line) and # * (search behind/forward). See Sunaku’s writeup for further details.

Layer optimized for Elixir code

Dusty Pomerleau posted on elixirforum about a layer optimized for writing Elixir code:

Elixir-optimized symbol layer.

(index fingers rest on ) and ])

As Dusty explains in the linked post, the following bigrams are inward rolls:

BEAKL 15

BEAKL (Balanced Effortless Advanced Keyboard Layout) is a sequence of optimized keyboard layouts, which include symbol layers. Here is the symbol layer for BEAKL 15:

BEAKL 15’s symbol layer.

(index fingers rest on ) and {)

Consistent with BEAKL’s philosophy, the layout favors the 3x3 “home block” with the index, middle, and ring fingers, while avoiding the pinky and center columns.

Multi-layer designs

Rather than a symbol layer, another approach is to distribute the symbols across several layers, particularly on smaller keyboards.

My symbol layer

This is my symbol layer, with index fingers resting on = and :.

My symbol layer.

Note, not pictured here: my base layer has keys for ' , . ; - / as usual and a _ key. See my keymap for full details.

I don’t claim it’s the best for you, but that this may be a point of inspiration for optimizing your own symbol layer.

The color tints show groups of related keys. I grouped these keys to try to make the layout easier to learn: comparisons ! < > =, arithmetic - + / *, bitwise operators & | ~, and brackets [ ] ( ) { }. The layout is geared for the programming languages I use: mainly C++ and some Python, shell, and LaTeX. Certainly, you should personalize your symbol layer for you.

Features:

This layout isn’t perfect. Some bugs:

Learning your symbol layer

As I mentioned, you won’t use keys that you forgot about. It’s important to learn your keyboard layers.

A reminder note with my symbol layer.

Whenever I change a keyboard layer, something that works for me is to write the layer on a slip of paper and put it below my monitor. The act of writing out the layer helps reinforce it in my mind, and the paper is there to refer to when I need a reminder. After a few weeks, I have it memorized and can throw out the paper.

Another way to learn is with typing practice. Try out your symbol layer on typingclub.com’s symbol practice or type-fu.com’s code lesson.

Symbol character frequencies

This section is on measuring character frequencies, which helps optimize the layout for the programming languages you use. Some symbols occur much more often than others. It’s helpful to know the distribution so that we can prioritize the layout for more frequent symbols.

I counted how often each symbol occurs in my own files. I found that this depends greatly on what kind of file it is. Below are character frequencies counted on six different kinds of text, each column counted on at least 40K of data. Units are percentages. “Prose” is a mix of email and other plain text files. “Mixed” is a mixed corpus.

     Prose       C/C++       Python      Shell       LaTeX       Mixed
#1   .  1.249    _  1.369    _  2.213    "  2.792    \  2.254    ,  1.416
#2   ,  0.952    *  1.238    .  2.183    -  2.430    {  1.327    .  1.067
#3   1  0.743    ,  1.200    ,  1.428    $  1.464    }  1.326    "  0.529
#4   0  0.713    )  1.151    )  1.248    0  1.386    .  0.932    _  0.497
#5   -  0.633    (  1.151    (  1.246    =  1.386    $  0.800    )  0.396
#6   2  0.514    .  1.037    '  0.844    1  1.333    ,  0.754    (  0.396
#7   )  0.487    /  0.991    "  0.784    _  1.250    _  0.454    -  0.362
#8   (  0.486    0  0.938    =  0.780    /  1.219    )  0.383    *  0.343
#9   6  0.396    ;  0.909    0  0.692    ]  1.095    (  0.372    '  0.335
#10  4  0.394    -  0.689    :  0.663    [  1.095    1  0.370    ;  0.317
#11  /  0.360    1  0.643    1  0.422    .  0.937    0  0.361    0  0.315
#12  8  0.306    =  0.589    2  0.373    #  0.898    2  0.344    /  0.290
#13  5  0.298    2  0.554    #  0.336    2  0.879    -  0.285    1  0.223
#14  3  0.281    3  0.361    [  0.332    \  0.823    :  0.232    =  0.210
#15  +  0.274    :  0.336    ]  0.329    ;  0.748    %  0.212    2  0.191
#16  [  0.246    4  0.321    -  0.303    :  0.658    ^  0.194    :  0.162
#17  ]  0.246    8  0.311    /  0.184    )  0.653    ~  0.176    \  0.150
#18  9  0.231    {  0.291    3  0.171    (  0.493    =  0.152    {  0.136
#19  7  0.219    }  0.291    *  0.162    ,  0.476    /  0.141    }  0.136
#20  :  0.210    5  0.288    5  0.141    3  0.473    [  0.131    3  0.115
#21  =  0.191    9  0.287    4  0.136    6  0.466    ]  0.128    4  0.101
#22  _  0.168    6  0.286    >  0.110    4  0.422    5  0.128    [  0.101
#23  |  0.137    +  0.282    6  0.108    '  0.422    3  0.113    ]  0.100
#24  '  0.098    >  0.272    `  0.081    5  0.362    &  0.110    5  0.093
#25  "  0.070    <  0.264    8  0.076    9  0.279    9  0.095    8  0.093
#26  *  0.064    [  0.262    +  0.076    8  0.260    7  0.094    6  0.090
#27  <  0.031    ]  0.262    7  0.072    7  0.250    4  0.088    !  0.089
#28  ?  0.020    "  0.251    \  0.057    |  0.248    6  0.087    9  0.087
#29  @  0.018    7  0.247    <  0.057    %  0.204    +  0.075    +  0.083
#30  ~  0.017    \  0.186    {  0.057    }  0.187    8  0.071    >  0.079

Caveat: What we actually care about are keys typed. But considering editor hotkeys, that’s not necessarily the same as characters written, which is what the above counts. For instance in Vim, many symbol keys are hotkeys in normal mode, like / for search.

Some observations from these stats:

For more stats, check out Xah Lee’s Computer Languages Character Frequency page, showing character distributions for over a dozen languages.

You can count character frequencies in your own files with this Python script: count_chars.py. The script reads the specified files and counts how often symbol characters occur. Use it like

python3 count_chars.py input.txt

Or to compute counts across multiple files, do

python3 count_chars.py file1.cpp file2.py file3.sh

Conclusion

We can apply principles from alpha key layout optimization to designing good symbol layers. The frequency of different symbol characters and bigrams depends a lot on what kind of text is being typed. So it makes sense to personalize your symbol layer to your needs. I hope this post gave some useful ideas on how to do that.

← More about keyboards