QMK Basics

QMK Basics: Tap dance, or how to let a key do more with one, two, three

New to QMK, or in for a refresher? In this series of tutorials on QMK Basics, you’ll be brought up to speed on a number of features that help you customize your keyboard just the way you want it.

Tap Dance is a feature that is able to make a key do something different depending on how many times you’ve pressed it. You might want to put some lesser used keys nearer to the home row, or maybe you’ve built a 40% keyboard and need to be creative with the key placement.

No matter the reason, the Tap Dance feature is a good way to add more utility to your keys. It can bring more flexibility than Tap and hold actions. It’s also one more way to add more functions to a key without having to switch to a different layer.

Basic Tap Dances

Often you’ll only need a key to do something different when tapped twice. ACTION_TAP_DANCE_DOUBLE(kc1, kc2) sends the first keycode when tapped once, and the second keycode when tapped twice.

When you want the second keycode to switch to a layer, you may use ACTION_TAP_DANCE_DUAL_ROLE(kc, layer). It sends the first keycode when tapped once, and switches to the given layer when tapped twice, the latter working just like TO(layer).

Don’t forget to add a way back to another layer when using this, as you might get stuck on the target layer if you forget to do so. You can plug out your keyboard and plug it back in to revert to your default layer, but it’s better to avoid the hassle by including a TO(layer) key on the target layer.

Limitations

These first two uses of Tap Dance are limited to sending Basic Keycodes only. You can work around this fact by using one of the advanced tap dances below.

Tapping term

The default tapping term is 200ms, which means that you’ll need to tap a key within 200ms of the previous tap in order for it to count in the tap dance. If you wait longer than the tapping term, it’ll finish the tap dance and send the keypress.

The tapping term is used to determine what keycode to send after pressing the tap dance key.

You can set your own tapping term with #define TAPPING_TERM 200 in your config.h file.

Advanced Tap Dances

The basic tap dances will likely cover most use cases. If they do, be sure to check out the Examples section down below to find out how to use them.

What if you have something specific in mind? Thankfully it’s easy to fully customize a tap dance, available in three flavours:

Customizing only at the finish

Use ACTION_TAP_DANCE_FN(fn) when you want to do something based on the number of taps. When a timespan with the length of TAPPING_TERM in milliseconds has passed since the most recent keypress, the function you passed as its argument is called.

With ACTION_TAP_DANCE_FN, your function is called after the last keypress times out. It can be after any number of keypresses, not just two like with ACTION_TAP_DANCE_DOUBLE.

The signature of the function you should write is void your_function_name(qk_tap_dance_state_t *state, void *user_data). Within the function, you’ll most likely want to know the number of keypresses: access the count variable with state->count. You can register the tap dance with ACTION_TAP_DANCE(your_function_name)

Customizing on each tap

You may want to do something on each single keypress, instead of just handling the total number of presses at the end. You may use ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn) to do so. Its signature offers more flexibility than the previous one:

  • on_each_tap_fn gets called every time you tap the tap dance key.
  • on_dance_finished_fn gets called when the tap dance has finished.
  • on_dance_reset_fn gets called when the tap dance action resets.

There are a number of callbacks you can supply as an argument, all of them optional.

Each function is optional and can be replaced with NULL if you don’t need to use that callback. For example, you might only want to handle each individual tap: ACTION_TAP_DANCE_FN_ADVANCED(my_tap_function, NULL, NULL).

The signature for the functions you need to write are the same as with the previous call: void your_function_name(qk_tap_dance_state_t *state, void *user_data).

I’d like to note a few things that can be of interest about the order of events and when they trigger. When you tap a tap dance key, first the on_each_tap_fn gets called, on keydown. It won’t be called on key up.

When a tap dance is finished, on_dance_finished_fn gets called. A tap dance is finished when one of two things happen:

  • A timespan longer than TAPPING_TERM has elapsed and no key has been pressed during that time.
  • A key other than the tap dance key was pressed within the TAPPING_TERM. The tap dance will be finished and state->interrupted will be true. Your custom function will still be called.
After a tapdance is interrupted by a key other than the tap dance key, all callbacks will still be called.

Lastly, when the tap dance is reset, on_dance_reset_fn is called. A tap dance will be reset:

  • Directly after a dance is finished (so directly after on_dance_finished is called).
  • state->finished was set to true during the last tap. You can manually set this in on_each_tap_fn if you want to. When you do this, on_dance_finished won’t be called, but on_dance_reset_fn will be called.

While you can make your tap dance skip the finished function, you can’t skip the reset: on_dance_reset_fn will always be called.

Customizing on each tap with a custom tapping term

You can set the global tapping term through TAPPING_TERM, but if you want to set a tapping term specifically for a tap dance, you can! Use ACTION_TAP_DANCE_FN_ADVANCED_TIME(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn, tap_specific_tapping_term).

This functions identically to the ACTION_TAP_DANCE_FN_ADVANCED function, with the addition of the tap_specific_tapping_term. Instead of referring to TAPPING_TERM for determining when a tap dance times out, it will use your custom value in milliseconds.

Examples

The steps on how to use Tap Dance are always the same.

  1. Make sure TAP_DANCE_ENABLE = yes is in the rules.mk file of your keymap. If you don’t yet have a rules.mk file, you can create it in the same directory as where your keymap.c file is: it’s just a text file and it doesn’t need any content besides TAP_DANCE_ENABLE = yes.
  2. Define an enum with the names of the tap dance sequences you want to make. You can choose the names yourself, I like to prefix the names with TD_ so I know it’s a tap dance enum value when looking at the keymap. Once you’ve declared the names, you can use them in your keymap with the keycode TD(your_tapdance_name).
  3. If you want to use one of the advanced ways that need a custom function, write those. They need to be declared before registering them, so the code in this step needs to be higher up in the file than the code from the next step.
  4. Register the tap dance sequences by adding a tap_dance_actions[] array.

You can look at the examples below to see how you can do this in your keymap.c.

Getting started: Tap once, tap twice

If you’re using a small keyboard like the Corne Keyboard, you could put some less often used keys on a layer. You could also map them with a tap dance, such as having Q send a TAB when double tapped:

enum {
  TD_Q_TAB = 0
};

qk_tap_dance_action_t tap_dance_actions[] = {
  [TD_Q_TAB] = ACTION_TAP_DANCE_DOUBLE(KC_Q, KC_TAB)
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [0] = LAYOUT(
    TD(TD_Q_TAB) \
  ),
};

In the documentation, a similar example reuses the Escape key to send Caps Lock when tapped twice. Handy for freeing up some keys closer to your hands, so you can replace them with keys you use more often.

When adding a tap dance to an alpha character (A-Z), it’s a good idea to pick a relatively rare character that you won’t often have to type twice back to back. In the example, I used Q because that letter often doesn’t occur two times in a row. The same goes for Z, J and X, based on a Wikipedia article about Letter Frequency.

Sending a shifted keycode

The two basic variants of the tap dance can only send Basic Keycodes. Thankfully, tap dances are fully customizable and so a workaround is easy.

The documentation provides an excellent example for precisely this scenario: Send : on Single Tap, ; on Double Tap. In the example, it first registers a shift modifier by register_code(KC_RSFT), and then sends the (now shifted) key of KC_SCLN. It also makes good use of the reset callback to unregister the keycodes.

Feel free to use that sample and replace the codes that are registered with the codes you want to use.

Safe reset

When you want to reset your firmware, you can press the reset button or short the RST pin with GND. But did you know you can also have a normal keyboard button as your reset button? You can use the RESET keycode for that (see Quantum Keycodes, a list of keycodes related to QMK itself).

It’s a good idea to place the reset button on a layer so you don’t accidentally hit it. If you want to be extra sure you don’t accidentally reset your board, you can use a tap dance for that. We can’t invoke the RESET keycode in a custom function like this, but thankfully reset_keyboard() is a built-in function that’ll reset the board for us:

enum {
  TD_RESET = 0
};

void safe_reset(qk_tap_dance_state_t *state, void *user_data) {
  if (state->count >= 3) {
    // Reset the keyboard if you tap the key more than three times
    reset_keyboard();
    reset_tap_dance(state);
  }
}

qk_tap_dance_action_t tap_dance_actions[] = {
  [TD_RESET] = ACTION_TAP_DANCE_FN(safe_reset)
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [0] = LAYOUT(
    TD(TD_RESET) \
  ),
};

Multiple tap dances

In the examples so far, we only had one tap dance. Registering multiple tap dances is easy, because tap_dance_actions is an array, separate the multiple tap dances with a comma:

enum {
  TD_Q_TAB = 0,
  TD_RESET
};

qk_tap_dance_action_t tap_dance_actions[] = {
  [TD_Q_TAB] = ACTION_TAP_DANCE_DOUBLE(KC_Q, KC_TAB),
  [TD_RESET] = ACTION_TAP_DANCE_FN(safe_reset)
};

Conclusion

Tap dances are a powerful way to customize key behaviour. With ACTION_TAP_DANCE_DOUBLE you can cover most use cases, sending one key on a single tap and another when tapped twice.

For more demanding cases, you can use ACTION_TAP_DANCE_FN and its advanced variants, specifying one or more callbacks to fully customize your tap dance.

Further reading

For advanced users, take a look at the Quad Function Tap-Dance by DanielGGordon. It allows for even more customizability, by not only handling taps, but also hold actions, providing double the possibilities.

When customizing your Tap Dance, the qk_tap_dance_state that is given as a parameter for your callback functions has more variables than just count. The struct is defined in qmk_firmware\quantum\process_keycode\process_tap_dance.h.

There exist many keymaps within QMK itself, added by users like you and I. You can search within the repository with a search term like ACTION_TAP_DANCE_FN or ACTION_TAP_DANCE_FN_ADVANCED to see more possible uses of these functions.

New to QMK, or in for a refresher? In this series of tutorials on QMK Basics, you’ll be brought up to speed on a number of features that help you customize your keyboard just the way you want it.