In Search of Hyperscrolling

2022/11/27

I was first introduced to hyperscrolling on the scroll wheel of my beloved G502 mouse. At first it was a novelty, but I’ve grown to appreciate hyper-fast scrolling when working with large documents/web pages. I use a trackball now as my primary pointing device due to ergonomics, since switching I’ve been looking for ways to keep the advantages of hyperscrolling; after venturing into custom firmware I think I’ve found a perfect solution for me.

Hyperscrolling

For those that haven’t had the pleasure, the scroll wheel on the G502, and several other Logitech mice, allows the user to both have great precision when scrolling slowly (like any ordinary scroll wheel) or to have great speed and scroll through documents very quickly with a flick of the finger. The G502 handles this by having a physical button under the scroll wheel that turns the scrolling detents on and off; in the off-state the wheel is allowed to free-spin with low friction - facilitating quick navigation. Newer mice like the MX Master 3 have engineered-out the need for the physical button and the mouse automatically unlocks the wheel for free-spinning when it is rotated quickly1. I find both implementations extremely satisfying to use and, overall, hyperscrolling allows me to navigate long documents easily when I’m already using my mouse for navigation.

Here’s a video featuring the G502 and demonstrating what I mean.

Problem

Now if hyperscrolling is so great then why is there anything else to say here?

  1. I now use a trackball instead of a mouse for ergonomic reasons
  2. Logitech makes trackballs, but, at least currently, they don’t have hyperscroll wheels

First solution - Linux only

For several years I used laptops running Ubuntu as my primary computers for doing work.

On those machines, I used the ScrollMethod and ScrollButton options of the libinput driver (see Ubuntu man page) to enable the behavior of using the trackball itself as a scroll wheel. This mode is enabled only when pushing the configured button and I mapped it to an extra button on the device.

The config looked something like this:

Section "InputClass"
  # ...
  MatchDriver   "libinput"

  # from man page: Enables a scroll method.
  #   Permitted values are none, twofinger, edge,  button.
  #   Not all  devices  support  all options, if an option is unsupported,
  #   the default scroll option for this device is used.
  Option        "ScrollMethod" "button"

  # from man page: Designates  a button as scroll button.
  #   If the ScrollMethod is button and the button is logically held down,
  #   x/y axis movement is converted into scroll events.
  Option        "ScrollButton" "11"
EndSection

This worked great for years. Then I switched to a Mac at work and also started to use my Windows desktop (using WSL) for personal projects at home wile also using my Linux laptop sometimes. As far as I can tell, neither Windows or macOS has native support for “ScrollButton” behavior and I’d like to have hyperscrolling on all three platforms.

Searching for a cross-platform solution

So I started looking for devices:

Even if there was support through device software for the scrolling I want, all of the software packages only run on Windows and macOS, so I’d still have to use the libinput option when using Linux machines.

Hardware support!

The solution? Just as hyperscrolling is a hardware feature on the Logitech mice, I realized that I needed to find an input device that has native support for what I want.

Customizable firmware

I eventually found the Ploopy classic-trackball. It is about the same size as my previous, Elecom trackball which is why I chose that specific model, but there are also other models in different sizes and configurations. It has a customized keymap from the developers to enable something they call “Drag Scroll”2 which sounded promising, but the firmware is also completely customizable so I knew I’d be able to configure it the way I want through writing some code.

I was worried about there only being five mouse buttons (left [MB1], middle[MB2], and right click[MB3] + back [MB4] and forward [MB5]) because I thought I’d have to lose one of those function to have an “enable drag scroll” button. I figured that even if that’s how the drag scroll keymap worked, I could customize the firmware to do something like “only go into drag scroll when forward and back are pressed at about the same time”.

That fear turned out to be unfounded! After building the drag scroll keymap and flashing it onto the device3, I found that the actual behavior allows the “forward” button still function normally if pressed and released quickly. When held it chords to activate a second layer of functionality where “left click” then puts the trackball into drag scroll mode and the forward event does not happen 4.

After discovering this, it was pretty easy to implementy my own keymap5 with the following changes from the stock drag scroll keymap:

The result is a device that I’m happy with. As another benefit on the Ploopy, the physical trackball is rolling on ball bearings so it can continue to spin when flicked. This combos very nicely with drag scroll mode.

Conclusion

At the end of all of this, I have a trackball:

Now I need one of these for my desk at work too - this seems to be the only downside.


  1. I think I remember reading that this is implemented with electromagnets. In any case, it makes for a very nice experience. ↩︎

  2. https://github.com/qmk/qmk_firmware/tree/master/keyboards/ploopyco/trackball/keymaps/drag_scroll ↩︎

  3. https://github.com/ploopyco/classic-trackball/wiki/Appendix-C%3A-QMK-Firmware-Programming ↩︎

  4. I don’t know how this really works, but from spying on keypresses with xev it seems the mouse is actually not sending “button 4 press” events exactly when the button is pressed and then sending the full “pressed, released” sequence only when the press and release happens within a quick period of time. Enabling drag scroll mode results in no events for the button being logged. I’m guessing that this behavior is coming from inside the qmk layers implementation which is invoked by the LT(1, KC_BTN5) call in the keymap ↩︎

  5. https://github.com/qmk/qmk_firmware/commit/f50d88967e1d9f75c39df45a76f5ea53f4a46826 ↩︎