MELON SOUR

About

Posts

Projects

Reducing Input Delay for the Keychron V1

Software

08/01/2024

software, qmk, mechanical keyboard, keychron

So I recently bought the Keychron v1 and noticed that Rtings lists it as having 20ms of input delay. For reference some 'gaming' keyboards advertise having a 1ms input delay and a good value seems to be less than 10ms. It's not that noticeable during normal use but it definitely feels a bit sluggish when playing games like Street Fighter where the responsiveness matters. Luckily I was able to reduce the input delay to a point where it doesn't bother me at all, here's how. The general gist is that the Keychron v1 runs with a software known as QMK, an open source firmware that controls microcontrollers on keyboards. With some research I was able to find a pull request that modifies the code to reduce input delay and after that I simply compiled the new firmware locally and used it to flash the keyboard. The instructions below will be tailored for Windows though the technique itself should be possible on Mac and Linux.

The Theory

The modification is based on this article by Michael Stapelberg which concludes input lag can be reduced by a faster debouncing algorithm and a lower USB polling interval. This is the example pull request stated in the article which I'll be replicating in the V1. It seems the defaults were chosen as conservative values but having used the modified firmware to write this post, I can safely say that the improved code works fine on the V1 without any erroneous input.

Modifying QMK Code

I followed the official documentation to edit, compile and flash the firmware. First I downloaded and installed the latest QMK_MSYS which seems to be an easy executable that prepares the CLI environment. I then forked the qmk repository to my own github account and then cloned it to my local machine.

$ git clone --recurse-submodules https://github.com/<my_username>/qmk_firmware.git

Then I executed the setup command

$ qmk setup

Then I made a new branch, navigated to /keyboards/keychron/v1/ansi_encoder and edited config.h and rules.mk according to the example pull request in the article. The encoder in the directory name refers to the knob variant of the v1; the same edits can be made in the ansi folder if you have the knobless v1.

// rules.mk DEBOUNCE_TYPE = sym_eager_pk // additionally enable VIA support if you use it to remap keys VIA_ENABLE = yes

Some mechanical key switches exhibit something known as contact bounce where due to a flaw in hardware, a single key press sends multiple repeating signals which results in duplicate characters being registered. You won't usually notice this at all because the QMK software has a debounce feature by default which mitigates this.

The default debounce algorithm is sym_defer_g which works by waiting until a set amount of time has passed where no changed has occurred, after a key is pressed. Once any potential contact bounced has settled, the input is pushed. This gets rid of the noise but it means every key press is delayed at minimum by the given debounce interval which is 5ms by default.

QMK provides alternate debounce algorithms and I went with was sym_eager_pk. This algorithm immediately sends the signal when a key is pressed, and ignores any subsequent bounce signals that happen afterwards within the debounce interval. This means that sym_eager_pk is faster by 5ms but the problem is that any contact bounces that happen after 5ms of the key press don't get filtered out (Which is usually the case). To counter this I increased the debounce interval to 50ms which completely eliminates any noise whilst retaining immediate responsiveness. The catch is that this prevents repeated key presses that are faster than 50ms but that's 20 individual inputs per second (without holding down the key) which doesn't happen unless you're rolling on an NES controller for Tetris.

// config.h #define USB_POLLING_INTERVAL_MS 1 #define DEBOUNCE 50

Finally I ran the compile command which builds the customized firmware as a .bin file in the root directory

$ qmk compile -kb keychron/v1/ansi_encoder -km default

Flashing the Keyboard

Now all I had to do was install the new firmware into the keyboard. First I installed QMK Toolbox then loaded the keychron_v1_ansi_encoder_default.bin firmware into it. Then I followed the Keychron documentation to flashing the keyboard which was basically as follows.

  • Hold fn + J + Z for 4 seconds to factory reset the keyboard then unplug the power.
  • Hold the reset button under the spacebar.
  • Plug in the keyboard and making sure DFU device connected is displayed in QMK Toolbox in yellow. (You can release the reset button once connected)
  • Press flash.
  • Hold fn + J + Z for 4 seconds again and factory reset for good measure.

And that's it, with the new firmware the v1 is noticeably more responsive. Though I don't have exact numbers to back this claim, it feels comparable to the Epomaker E84 I was using before so I'm guessing it's somewhere between 10ms and the original 20ms. If you experience any instability you can simply reflash the firmware with the original firmware.

Lastly the official site gives the following disclaimer so I'm obligated to say that there's a small but inherent risk to overriding the firmware if you feel like trying this mod out for yourself.

Note: If everything works fine with your keyboard. Please don’t flash the firmware. There is a chance it can damage your keyboard.