← More about keyboards

Certain topics come up pretty regularly on r/olkb, so I’ve collected some thoughts here. Each section includes a section link 🔗 for easy sharing.

If you don’t find what you are looking for here, try also the Keymap FAQ in the QMK documentation and Links about keyboards.

Meta: How to ask for help

🔗 Link

r/olkb and r/ErgoMechKeyboards are great places to ask for keyboard troubleshooting help. Here are some suggestions to make it easier for others to help you:

Good luck!

Keyboard shopping tips

🔗 Link

For typing comfort, I highly recommend a keyboard that is split, columnar, and with QMK or other programmable firmware. Here is a tour of what makes these keyboards so good. An example is the ZSA Moonlander keyboard. Some other popular examples are the Lily58, Sofle, and Corne split keyboards.

ZSA Moonlander

The r/ErgoMechKeyboards wiki has a list of vendors that sell split and ergonomic keyboards, organized by region. For yet more options there is an extensive vendor list in Keyboard Builders’ Digest. You can also try r/mechmarket.

A few notes to give fair warning…

How long to get used to a split columnar keyboard?

🔗 Link

It took me a couple weeks of use to adapt to typing on a split columnar keyboard. Others have reported similar experience. My first such keyboard was a Dactyl Ergodox, which in addition to being split and columnar is also concave.

Dactyl Ergodox

I was fumbling at first. It took practice and patience to develop the right muscle memory. Additionally, if there are non-standard quirks to your typing habits, you might need to correct for that. I initially wanted to type the key in Y QWERTY position with my left hand, but on a split keyboard that cross-hand jump becomes a big one!

But it’s definitely worth it. A columnar split keyboard makes it easier to keep wrists straight while typing with hands at a relaxed shoulder width apart. It’s the most effective change I’ve made for my typing comfort.

Should I use a wrist rest?

🔗 Link

Wrist rests are not necessary for good ergonomics. I don’t use them myself. If anything, a wrist rest should support the heel or palm of the hand, not the wrists. Pressure on the soft area of the wrist puts pressure on the tendons and nerves.

From Ergonomic Usage of Wrist Rests and Palm Supports:

In the vast majority of cases, wrist rests do not provide any significant ergonomic benefit and in fact will usually increase the number of risk factors for injury in your computer workstation. The reason is that if you ‘rest’ your ‘wrist’ on any type of support, be it foam, gel, webbing, cloth, etc. you are applying pressure to the underside of your wrist which will compress the tissues, resulting in decreased blood flow. More specifically, you can compress the carpal tunnel and possibly pinch the median nerve, which can lead not only to long term injury, but short term symptoms such as tingling, numbness or coldness in the hands, and finger muscles which fatigue quicker due to reduced circulation.

OSHA on Wrist/Palm Supports:

Potential Hazards

Possible Solutions

Related reading:

Should I learn how to touch type?

🔗 Link

Do you want better speed, accuracy, and typing comfort? Then the answer is yes. Learning proper touch typing technique helps all of that.

Touch typing is a style of typing. The eight fingers are placed along the home row, and each finger has designated keys that it can reach. This systematic method lets you type each key with little motion and by sense of touch alone, enabling you to type faster and more efficiently. Additionally, it reduces neck strain and improves posture by keeping your eyes on the screen, not constantly glancing at the keyboard to find the keys.

Resources to learn touch typing:

Related reading:

I type XXX wpm – Is that fast?

🔗 Link

How fast is “fast”? The typing site onlinetyping.org published distributions of typing speed, based on tests on their site. By this data, if you can type over 70 words per minute (wpm), you are faster than about 95% of people. But see also the caveats below.

Some people can type well over 100 wpm. A few can type over 200 wpm. If you include stenography, some fast stenotypists can type over 300 wpm.

Want to type faster?—see the resources for Should I learn how to touch type?

Caveats:

A lot of factors influence typing speed. Some big ones:

Sources:

Pressing a key sometimes types it twice

🔗 Link

When you press a key once, does it sometimes type twice (or more)? A common cause is contact bounce (or chatter) in the key switch. Electrical contacts are usually springy material. When the switch is pressed, the contacts bounce, making and breaking contact for some milliseconds before settling to a steady contact. So rather than a clean 0 to 1 transition, the raw digital reading bounces between 0 and 1.

A similar bouncing problem occurs in the other direction when the key is released. The settling time depends on the kind of switch and may be different for press vs. release. Additionally, the settling time tends to increase as the switch is worn from use. Some keys are pressed more frequently than others (extreme example: E key vs. the [ key), so switches wear down at different rates. This can explain observing bouncing on some keys but not others, especially if you reused some switches.

Diagnosis: Go to the QMK Configurator test page and press each key (this works also for non-QMK keyboards). If any key events appear suspiciously fast, a message appears “CHATTER HAS BEEN DETECTED” and the key is highlighted red.

Software troubleshooting: It’s typical to apply filtering algorithms in software to “debounce” the button. (See the Related reading below for several algorithms.)

Hardware troubleshooting:

Related reading:

How much lag do custom keyboards have?

🔗 Link

And related question, are custom keyboards good for gaming? I’ll focus on keyboards running QMK firmware, but this is probably representative in general since other keyboard firmwares work similarly under the same constraints.

Input lag of a keyboard is the time between actuating a key to the computer receiving the key report. Input lag with QMK is typically 2–14 ms, so fast that it is visually imperceptible, less than one frame of 60 fps video (16.7 ms). QMK keeps up even with the most demanding games. Lag due to keyboard firmware is probably negligible compared to other sources of lag (more on this below).

The following processes are the main sources of lag. See Michael Stapelberg’s investigation for deeper discussion:

  1. Matrix scanning continually checks the keyboard matrix circuit for changes in key state. The time between successive scans in on the order of 0.2–2 ms. A faster keyboard microcontroller can improve this.

  2. Debouncing is applied to clean up the matrix changes. As a key is pressed, the electrical contacts typically make and breaking contact several times. By default, QMK debounces the signal by waiting for a debounce time of 5 ms, while some keyboards are configured for a longer debounce time. Setting an “eager” debouncing strategy avoids this latency. You can also try reducing the debounce time, though beware that keys might bounce if the time is too short.

  3. USB polling transfers key reports to the computer. Polling defaults to once every 1 ms, the fastest interval supported by USB Full Speed (USB 1.0).

Beware that input lag is much larger on keys using certain special QMK features, namely Tap-Hold keys, Tap Dance keys, Auto Shifted keys, Space Cadet keys, and keys used in Combos. Avoid using them on your gaming layer. Event processing for keys using these features is delayed while QMK waits to determine whether the key is being tapped vs. held or whether other keys are pressed at the same time. This delay may be substantial, up to TAPPING_TERM or 200 ms by default for tap-hold keys or COMBO_TERM or 40 ms by default for keys used in combos.

Other sources of lag:

Related reading:

Home row mods are hard to use

🔗 Link

It’s not just you. Home row mods are tricky for most people. Configuration is critical, and on top of that it takes time to get used to it.

Home row mods are nontrivial to use reliably because of accidental mod triggers during fast typing. You might think of normal typing as pressing and releasing one key at a time like “T down, T up, H down, H up.” But fast typing is often sloppier than this like “T down, H down, T up, H up,” especially in “rolled” sequences of adjacent keys like io or ast in QWERTY. There’s also the complementary problem of failing to trigger a mod when it was intended.

If you haven’t already, read Precondition’s guide to home row mods. It explains a bunch of tap-hold configurations and variations to mitigate these problems.

Suggested settings:

In addition to good configuration, I suggest you may need a couple months to get used to home row mods. Everyone’s typing habits are a little different. I struggled badly when I started with accidental mod triggers, despite using Precondition’s recommended settings. But after a couple months of persistence, my typing adapted. Now I use home row mods with confidence.

Other tips:

Alternatives: If home row mods are not for you, here are several alternative approaches to modifiers that you might consider.

Firmware is too large!

🔗 Link

A build error like “firmware is too large! X bytes over” means the compiled firmware is too large to fit in the microcontroller’s programmable flash memory. This is easily a problem on microcontrollers with less than 32KB flash space.

This suggestion may come too late, but when building a keyboard, it’s worth using a microcontroller with larger flash space to avoid this problem in the first place. Some QMK-compatible options with more space are Proton C (STM32F303xC) with 256KB flash, WeAct Blackpill (STM32F411) with 512KB flash, and Nice Nano (nRF52840) with 1MB flash. See also QMK Compatible Microcontrollers and ZMK Supported Hardware.

Otherwise, you need to reduce the firmware size. The main contributors to firmware size are enabled features. By “feature,” I mean a behavior that can be independently enabled, like Mouse Keys and Space Cadet Shift in QMK. Each enabled feature adds around 1000–4000 bytes, depending on what it is. So a good way to reduce firmware size is to cull unused and non-essential features.

Specific to QMK: Make sure to enable LTO (link-time optimization) in rules.mk:

LTO_ENABLE = yes

Firmware will take longer to build, but the result will be smaller. See Squeezing the most out of AVR (applicable also with non-AVR MCUs) for further tips.

MT doesn’t work with this keycode (QMK)

🔗 Link

With mod-tap keys MT(mod, kc) and layer-tap keys LT(layer, kc), a limitation is that the tapping keycode kc must be a basic keycode, a keycode in the 0–255 range. All keycodes in QMK are 16 bits, so in order to pack identifiers, the mod or layer, and kc in that space, the kc portion is limited to 8 bits. As described in the Caveats section:

Currently, the kc argument of MT() is limited to the Basic Keycode set, meaning you can’t use keycodes like LCTL(), KC_TILD, or anything greater than 0xFF. This is because QMK uses 16-bit keycodes, of which 3 bits are used for the function identifier, 1 bit for selecting right or left mods, and 4 bits to tell which mods are used, leaving only 8 bits for the keycode.

Shifted keycodes like KC_QUES (= Shift + /) and user-defined keycodes are not basic keycodes, so attempting “LT(layer, KC_QUES)” or “LSFT_T(MYMACRO)” is unsupported. Fortunately, there is a workaround.

MT with a non-basic tapping key

First, pick a basic keycode like KC_0 as a placeholder for the tapping keycode kc. This keycode will never be sent, so any arbitrary basic keycode will do. Then change the tap function to behave as desired.

In keymap.c:

// Copyright 2023 Google LLC.
// SPDX-License-Identifier: Apache-2.0

// Define named constants for our customized tap-hold keys.
#define NAV_QUES     LT(NAV, KC_0)
#define SFT_MYMACRO  LSFT_T(KC_1)

// Use `NAV_QUES` and `SFT_MYMACRO` in your layout...

bool process_record_user(uint16_t keycode, keyrecord_t* record) {
  switch (keycode) {
    case NAV_QUES:  // NAV layer on hold, KC_QUES on tap.
      if (record->tap.count) {  // On tap.
        if (record->event.pressed) {  // On press.
          tap_code16(KC_QUES);
        }
        return false;  // Skip default handling.
      }
      break;
 
    case SFT_MYMACRO:  // LSFT on hold, MYMACRO on tap.
      if (record->tap.count) {  // On tap.
        // Your macro here...
        if (record->event.pressed) {
          SEND_STRING("keyboards!");
        }
        return false;  // Skip default handling.
      }
      break;
 
    // Other macros...
  }
  return true;  // Continue default handling.
}
One-shot mod-tap OSM + MT

Attempting a one-shot mod-tap as keycode “MT(OSM(mod), kc)” is unsupported, but can be done through changing the hold function. Here is an example in which the D and K keys are one-shot Shift mods when held.

// Copyright 2022-2023 Google LLC.
// SPDX-License-Identifier: Apache-2.0

// Replaces mod-tap key's hold function with its one-shot counterpart.
static bool oneshot_mod_tap(uint16_t keycode, keyrecord_t* record) {
  if (record->tap.count == 0) {  // Key is being held.
    if (record->event.pressed) {
      const uint8_t mods = QK_MOD_TAP_GET_MODS(keycode);
      add_oneshot_mods(((mods & 0x10) == 0) ? mods : (mods << 4));
    }
    return false;  // Skip default handling.
  }
  return true;  // Continue default handling.
}

bool process_record_user(uint16_t keycode, keyrecord_t* record) {
  switch (keycode) {
    case LSFT_T(KC_D):
    case RSFT_T(KC_K):
      return oneshot_mod_tap(keycode, record);
  }
  return true;
}

See also rafaelromao’s keymap, which implements one-shot mod-tap keys.

Layer-mod-tap LM + LT

QMK has a layer-mod LM(layer, mod) key that changes layers with a mod held. There is no predefined keycode that performs as LM on hold and some other key on tap, but such behavior is possible, again, through changing the hold function.

In this example, SFT_NUM_DEL is a key where the hold function is to switch to the NUM layer with left Shift applied, and the tap function is KC_DEL.

// Copyright 2024 Google LLC.
// SPDX-License-Identifier: Apache-2.0

// Define named constant for our customized tap-hold key.
#define SFT_NUM_DEL  LT(NUM, KC_DEL)

// Use `SFT_NUM_DEL` in your layout...

bool process_record_user(uint16_t keycode, keyrecord_t* record) {
  switch (keycode) {
    // Switch to the NUM layer with LSFT applied on hold, KC_DEL on tap.
    case SFT_NUM_DEL:
      if (record->tap.count == 0) {           // On hold.
        if (record->event.pressed) {          // On press.
          register_mod(MOD_BIT(KC_LSFT));     // Hold left Shift.
        } else {                              // On release.
          unregister_mod(MOD_BIT(KC_LSFT));   // Release left Shift.
        }
      }
      // Continue default handling, which switches to NUM on hold and
      // performs KC_DEL when tapped.
      return true;

    // Other macros...
  }
  return true;  // Continue default handling.
}

How to type non-English letters (QMK)

🔗 Link

There is a good solution for typing most symbols in major Western European languages by using the US-International layout. For other symbols, a possibility is QMK’s Unicode input feature. See Typing non-English letters.

Some keys are swapped (QMK)

🔗 Link

Magic keys: Magic keys and a related Command feature are capable of dynamically disabling GUI keys, swapping Ctrl ↔︎ Caps Lock, Alt ↔︎ GUI, and a few other similar such things. A common source of confusion is when these settings are changed unintentionally. Furthermore, magic settings are saved to EEPROM, so they stubbornly survive even when the keyboard is flashed with new firmware. The way to fix this is to clear the EEPROM, which resets any magic settings. Two ways to do that:

See also Some Of My Keys Are Swapped Or Not Working.

Non-US QWERTY host layout. The usual “KC_”-prefixed keycodes assume the host computer is configured to US QWERTY keyboard layout, see What Are the Default Keycodes. Configuring the computer to a different layout can result in swapped keys or some shifted keys producing unexpected results. See Using non-US layouts.

SEND_STRING doesn’t work (QMK)

🔗 Link

There are some gotchas with SEND_STRING():

What is this weird C syntax (QMK)

🔗 Link

QMK code is embedded code written in C11, the 2011 standard of the C programming language, with nonstandard GNU extensions. You may run into some unfamiliar syntax even if you are otherwise versed in C/C++:

← More about keyboards