Building my ultimate keyboard ★

Contents
What comes to mind when you see the description “the ultimate keyboard”?
There are many keyboards in this world; here are some that might fit the “ultimate” moniker:
- Das Keyboard 4 Ultimate mechanical keyboard
- DataHand
- Ergodox EZ
- Glove80
- Happy Hacking Keyboard
- Model M Keyboard
- Stenotype keyboards.
- Ultimate Hacking Keyboard
- CharaChorder
Some even have “ultimate” in their name, although I’ll assert that they’re far from ultimate.
Any man who must say, “I am the King”, is no true king.
I’ll go one step further to say that no keyboard is universally the ultimate because it’s impossible to agree on how to rank different keyboards. For example, while I personally prefer a split keyboard, you might not. Some people have very long fingers and some have very short fingers, making some layouts more preferable. Others may not even have 10 fingers (or both hands), requiring more drastic modifications.
If an ultimate keyboard exists, it differs from person to person. This is my attempt to build my ultimate keyboard.
My wishlist
To me, the ultimate keyboard should have these features:
-
Should be split to support a more natural typing position.
Really the biggest ergonomical leap in my opinion.
-
Customized for my own fingers and typing eccentricities.
Column stagger, curvatures and tenting are features I think I want but they need to be tuned, probably by trial-and-error. The position of the thumb keys is another sticking point that the other keyboards I’ve tried have failed to get just right.
-
Have an integrated trackball or trackpad.
This way I don’t have to move my hand so far and I can free up some valuable desk space. It shouldn’t be operated with my thumb due to my RSI.
-
Contain the keys I need but no more.
I like smaller keyboards and I’ve been very happy and with my custom keyboard layout that only has 34 keys. Some modifications are fine of course but for the most part I want to be able to use the same layout on both the Ferris and my new keyboard.
- To fulfill these requirements I need to be able to customize all parts of the keyboard and I really don’t want to learn CAD and create one from scratch; I wonder what alternatives I have?
Cosmos keyboard configurator
Having looked around, I probably want something similar to a Dactyl / Dactyl Manuform (many variants exists). They’re keyboards you generate from parameters (such as number of rows and columns and the amount of curvature). I’ve always wanted to try one and now with a 3D printer, I can.
When looking for a generator I stumbled upon the Cosmos keyboard configurator and I want to gush about it a little because it’s excellent.

It’s excellent because it allows a clueless sod like me to configure a keyboard the way I want to and it has an impressive feature list:
- Easily generate keyboards of any size.
- Customize XY-spacing, row- and colomn curvature, and more.
- Several pre-made thumb clusters.
- UI to move around all the keys.
- Supports different switches (I so need my Choc switches).
-
An
Expert
mode that allows you to customize anything via JavaScript. - Supports encoders, trackpads, OLED displays, and trackballs.
- Can generate a wrist rest.
-
Exports
.stl
for easy printing or.step
you can import to CAD.
Here’s a small snippet from how the code in Expert
mode might look like:
const = curvatureOfColumn: ,
curvatureOfRow: ,
spacingOfRows: , // 18x19 Choc spacing
spacingOfColumns: ,
arc: ,
};
/**
* Useful for setting a different curvature
* for the pinky keys.
*/
const = ...curvature,
curvatureOfColumn: ,
};
/**
* The plane used to position the upper keys.
* It's rotated by the tenting and x rotation
* then translated by the z offset.
*/
const = new
// `20` specifies the tenting angle.
.
.
.;
The entire state of the keyboard is also stored in the url, so I can easily share my config by including a link: Cosmos reference of the final keyboard configuration. (Barring any breaking changes in the tool of course…)
Initial design parameters
Even with a keyboard configurator I needed a way to start. I already have a layout that I really like so I wasn’t starting from nothing. These were the important parts going into the design process:
-
A 3x5 grid with 1-2 thumb keys (in practice one thumb key is enough).
If you question why I want to build such a small keyboard I’ll redirect you to the discussion in The T-34 keyboard layout post.
-
Integrated trackball on the right-hand side.
-
Choc switches.
One of the major decisions with a keyboard is what kind of switch to use. While MX-style switches are the most common I personally really love Choc switches for a couple of reasons:
- Low-profile
- Low actuation force
- Can be closer together
While a low profile switch is more important for a flat keyboard, not a tented and curved one like I’m building now, the flatter keycaps and the switches being closer together is crucial for pressing two keys with one finger:
A horizontal combo is pressed with the finger in the middle of the keys. It’s surprisingly comfortable.
The low-actuation force is also more comfortable to me as it helps reduce the strain on my fingers, and makes combos (pressing several switches at once) generally more pleasant.
Hardware and material
It’s not enough with just a 3D printer, to build a working keyboard you need a bunch of hardware:
-
Two microcontrollers.
I got the Liatris microcontroller as it has enough pins to connect a trackball sensor and it supports QMK.
-
Switches
What kind of Choc switch should I use?
Linear, tactile, or clicky?
Exactly how heavy should they be?
Should they be silent?I wasn’t sure so I ordered a sampling of different switches to try.
A collection of different Choc switches. For the final keyboard I used the Ambients silent Noctural (linear / 20gf) switches, where the deciding factor was getting as light switches as possible. (I’ve previously used modded 15gf switches, which were even better, but I couldn’t find a way to buy them.)
-
Keycaps
It’s hard to decide on a colorscheme so I bought a bunch of random colors. Keycaps aren’t only for looking cool. A convex keycap for the thumb button instead of the standard concave one makes it much more comfortable:
The blue convex keycap to the left and the red concave to the right. I also got keycaps for the index row with these small homing notches to help my fingers more easily find the home row.
-
A pair of TRRS connectors and a TRRS cable.
-
A Trackball with a matching sensor.
I decided to pick up this PMW3389 sensor because it was recommended in the keyboard configurator and a red 34mm trackball from Amazon.
-
Filament for the 3D printed pieces.
I ended up settling on the PolyTerra PLA Army Purple for the case but I used a bunch of different filament during the prototype phase.
-
Diodes, screws, heatset inserts, and cable to do the wiring.
Prototypes

When you’re trying to design something like a custom keyboard I think you need to go through a bunch of trial-and-error until you find something that fits.
Here’s a short rundown of some of the significant revisions I went through, mostly to illustrate that it’s very much an iterative process.
First print
For my first print I mostly wanted to print it out and test how a keyboard with a standard curvature felt. I also wanted to try to place a trackball somewhere.
I ended up removing a regular thumb key (I’ve used two thumb keys with my keyboard layout) to make it fit and I added a “mouse thumb key” that I plan to use as a left mouse button
when I’m operating the trackball.
It was tricky to place the trackball as I wanted to operate it with my index + middle finger, not my thumb.
Another tweak I made was to reduce the spacing between the keys to be closer to the Choc spacing. Choc spacing seems to be 18.6 x 17.6 mm, but I used 19 x 18 mm spacing—the attraction to round numbers is real.

Pressing the top right key with the ring finger
Most of the keys on the keyboard felt fine but I had one major annoyance: I have a habit of using the ring finger to press the top right key instead of the pinky but with the curvature on the keyboard this just wasn’t possible anymore.
You might wonder, why don’t I just create a new habit and use the pinky as you’re supposed to? The simple answer is that I hate it. To my fingers that feels beyond terrible and I’d rather remove the key and only have two keys in the outermost column. As it happens, pressing the key with my ring finger (on a flat keyboard) feels good so I’d rather adjust the key than remove it.


p
key compared the other keys in the column.
I also added an extra mouse thumb key and lowered the pinky column a bit.
Adjust mouse keys and increase tenting

Pressing p
with my ring finger feels great.
Pressing the thumb normal thumb key feels awful because the mouse thumb keys are in the way when I relax my hand.
Adjustments made:
- Moved mouse thumb keys to be more vertical and pressed from the side.
- Added an extra pinky key reachable when I’m using the trackball.
- Increased tenting to 20 degrees from 10 degrees.

Rounded base and pinky tweak

- I tried the “rounded” sides and top feature of Cosmos.
- The mouse pinky key was too low, I raised it up a bunch.
Thumb keys adjustments

- Rotated the main thumb key inwards
- Added an area for a display
- Lowered the mouse thumb keys a little
- Removed the “rounded” features
More tweaks and the left half

- Configure the left half of the keyboard
- Move pinky keys a little upwards
- Move all thumb keys a little further away
- Removed the display (felt like too much of a hassle for a little coolness)
Although I said I wanted to have a 3x5 grid, the generator included an easy option to include a small bottom row with 2 extra keys (for the ring and middle finger) that I wanted to try out for the left side. They’re… Okay I guess. Not crazy uncomfortable but not quite comfortable enough that I want to have common keys there.
Beta V3

At this point the Beta V3 of configurator is out and in it there’s several improvements, most notably:
-
Both halves can be configured at the same time.
-
Can go between the Advanced and Expert tabs! WOW!
I had to manually keep track of the JavaScript changes I made, and update them manually if I wanted to make a change in the UI… But no more!
I had to redo most of the configuration and I think I made some minor changes that I didn’t keep track of, but I made two larger ones:
- Lowered the tenting angle to 15 degrees (from 20)
- Lowered ring pinky column key a little
Small tweaks to pinky and thumb keys

- Raise/tilt top pinky row key
- Move thumb keys on left side closer together
Trackball mounting types

When I started this project Cosmos only supported a single type of trackball mount: roller bearings. They worked quite poorly for me as the ball was spinning well in one direction but poorly in others.
Luckily new options were added and as I’m writing this there’s 4 different ways you can mount the trackball:
- Roller bearings (the old option)
- BTU (7.5mm or 9mm)
- Static ball bearings (3.175mm)
Because I was burned with the bad experience (and I didn’t want to rebuild the keyboard yet again) I made small prototypes of the three different options:

The BTUs had the least friction and it felt really easy to spin the ball but they were also distressingly loud. The static ball bearings had more friction than the BTUs and less than the roller bearings while being completely silent, so I chose to go with the ball bearings.

While they don’t feel nearly as good as the Kensington SlimBlade they’re decent enough. I try not to use the mouse that much and having the trackball so much closer is worth it compared to having a separate trackball unit besides the keyboard.
Remove mouse keys

After having used the keyboard for real I realized that the three keys dedicated to mouse buttons would have to go. There were two major issues with them:
- The pinky key got in the way sometimes when I reached for the top column, and I couldn’t retrain myself to avoid it.
- One of the goals with the keyboard layout I use is to reduce the thumb and pinky usage of my right hand. The mouse keys counteract this goal.
So I had them removed and I rewired the right half for the 3rd time. Sigh.
I think the lesson is that it’s not enough to print a prototype and press switches pretending to type, you have to build and use the keyboard a bunch before you can evaluate some of the design decisions.
Additional printed parts
While the case is the biggest and most important part of this kind of keyboard, there are a few other parts I had to print to complete the keyboard.
Wrist rests

Magnet attachments


The wrist rests didn’t come with any sort of attachment to the case, so they just always drifted away. I tried to combat this by gluing magnets inside the case and outside the wrist rest, making them stick together just enough to stay together during normal use, while being easily removable.
I ended up not using the wrist rests
Despite my efforts, I haven’t been using the printed rests as I reverted to the ”squishy” ones I’ve used before:

The printed felt too uncomfortable and I couldn’t find an angle I liked more than the gel rests. Oh well.
Microcontroller holder

There’s a holder to fasten the microcontroller to the case that I use.
I had to manually make a hole to make the Boot
button accessible, which was easily accomplished when slicing the model.
Bottom plate

One problem with the Ferris was that it would sometimes slip on the table. I counteracted this by using an old Netrunner playmat but I wanted another solution.
The keyboard is generated with a bottom plate that’s used to hide and protect the internals. I printed it in TPU, a flexible and rubbery material, that gives enough grip to stay relatively still when I’m typing.
Wiring

Matrix
One of the first things you need to do when wiring up a custom keyboard is to plan out a matrix. I guess you could directly wire every switch directly to the controller too, but that’s not feasible if you have a larger amount of keys, so the usual thing is to use a matrix.
What a matrix means is you should wire together all keys in a row and connect that to a pin on the controller, and to the same with the columns.
It might look something like this:


The green lines indicate columns and the purple lines indicates rows.
You should also use diodes in the matrix (for either rows or columns, I chose the rows). Pay attention to the diode direction.




The wiring is horrible, I know.
I only lost one microcontroller due to a short… With my wiring prowess I consider that a success!
Controller wiring


Controller pin | Connection |
---|---|
1 | Handedness (VCC on the left keyboard and GND on the right) |
2 | TRRS data |
3, 4, 5, 6, 7 | Matrix columns |
20, 22, 26, 27 | Matrix rows |
13 (CS1) | Trackball SS |
14 (SCK1) | Trackball SCK |
15 (TX1) | Trackball MOSI |
16 (RX1) | Trackball MISO |


Adding the keyboard to QMK
The QMK cli has the qmk new-keyboard
command that helps you get started.
I couldn’t get the generated template to work for me, so I copied settings from an existing keybord with rp2042
support.
I’ll try to hit on the most important parts of the config, take a look at the source code for all details.
Basic setup
The folder structure for the keyboard looks like this:
cybershard
├── keyboard.json
├── rules.mk
├── halconf.h
├── mcuconf.h
└── keymaps
└── default
├── config.h
├── keymap.c
├── rules.mk
└── ...
(Cybershard is the name I eventually settled on for the keyboard.)
The most important part is keyboard.json
that defines (almost) everything we need for a new keyboard in QMK.
First you need to set the processor
, bootloader
, and usb
values.
The Liatris microcontroller uses the RP2040 MCU, and I just picked some vendor- and product identifiers:
},
}
Then we need to define the matrix (with the pins we soldered) and the layout (how we’ll configure the keymap in keymap.c
):
// We need to use a `GP` prefix for the pins.
},
// First physical row
,
,
,
,
,
// Second row
,
,
,
,
,
// etc...
]
}
}
}
Note that we can pick whatever physical pins we want as we can move around and configure them in software.
The LAYOUT
macro is what we use in keymap.c
to define our keymap.
When defining it we can choose to skip certain keys and reorganize it to be easier to define; for example, there’s no switch at 0,0
in my keyboard so I skip that.
The above LAYOUT
can then be used like this:
SE_J, SE_C, SE_Y, SE_F, SE_P,
SE_R, SE_S, SE_T, SE_H, SE_K,
SE_COMM, SE_V, SE_G, SE_D, SE_B,
SE_A, SE_B,
// Thumb keys
FUN_CLR, MT_SPC,
,
Flashing
With the above setup we should be able to flash the keyboard by first entering the boot loader and running:
qmk flash -kb cybershard -km default
Now the process of updating the firmware is quite nice and unless I screw up I don’t need to connect another keyboard to do it.
-
Start flashing with
qmk flash
(it will wait until it finds a flashable target). -
Press the
QK_BOOT
combo (the keyboard becomes unresponsive). - Wait until the script finishes and the keyboard is available again.
Split keyboard
To get the split keyboard feature to work I had to set the SERIAL_DRIVER
option in rules.mk
:
SERIAL_DRIVER = vendor
And add the split
configuration to keyboard.json
and modify the LAYOUT
macro:
// The pin that signals if the current controller is the left (high)
// or right (low) controller.
},
// The TRRS data pin.
// We can override the pins for the right controller.
// Note that GP26 and GP27 are swapped compared to the left side
// due to a mistake I made when soldering.
}
},
// We need to sync the matrix state to allow combos, mods, and
// other stuff to work.
}
}
},
// The rows 0 to 3 specifies rows on the left side and
// 4 to 7 the rows on the right side.
// These 5 keys are the first row on the left side.
,
,
,
,
,
// These 5 keys are the first row on the right side.
,
,
,
,
,
// etc..
]
}
}
}
The LAYOUT
macro is just a function with many arguments but with the right order it can be formatted
to look similar to the physical keyboard.
For example, this is how the base layer of my keyboard could look like:
// Left side // Right side
SE_J, SE_C, SE_Y, SE_F, SE_P, SE_X, SE_W, SE_O, SE_U, SE_DOT,
SE_R, SE_S, SE_T, SE_H, SE_K, SE_M, SE_N, SE_A, SE_I, REPEAT,
SE_COMM, SE_V, SE_G, SE_D, SE_B, SE_SLSH, SE_L, SE_LPRN, SE_RPRN, SE_UNDS,
// The extra two keys on the left side
SE_MINS, SE_PLUS,
// Left thumb keys // Right thumb key
FUN_CLR, MT_SPC, SE_E
,
Trackball
It took a long time for me to get the trackball working (admittedly, mostly because I soldered the pins wrong). There’s quite a lot of documentation for QMK but curiously enough I didn’t find anything that covered the whole setup. I arrived here by trial and error, trying to piece together parts from other keyboards into a setup that worked for me.
First we need to create the files halconf.h
and mcuconf.h
(they go in the same folder as keyboard.json
) to enable the SPI driver:
And enable the pointing device with the pmw3389
device driver in rules.mk
POINTING_DEVICE_ENABLE = yes
POINTING_DEVICE_DRIVER = pmw3389
Now we need to add the sensor pins to config.h
:
// SPI1, matching mcuconf.h
// The pin connections from the pmw3389 sensor
This should be enough to get the sensor going, but because we have a split keyboard we need to set that up too:
// The trackball is on the right
There are some additional tweaks that I had to play with to make the trackball work well:
// The trackball is quite sensitive to how
// large the liftoff distance should be.
// Sets the mouse resolution, up to 16000.
// The directions where messed up, this fixes it.
With that I got the trackball moves the mouse as expected.
Debug
As I struggled to get the trackball working I tried to use the debug output. I’ll include it here for completeness sake:
-
Enable the console in
rules.mk
:CONSOLE_ENABLE = yes -
Enable pointing device debugging in
config.h
: -
Turn on debugging in
keymap.c
:voiddebug_enable = true;debug_mouse = true;
And then run qmk console
from the command line.
Is this the ultimate keyboard?
No.
This keyboard is certainly the most comfortable keyboard I’ve used but it’s not close to being an “ultimate” keyboard. Here’s a few things that might improve the keyboard:
-
The trackball still isn’t nearly as comfortable as the Kensington SlimBlade.
Maybe a keyboard with a larger trackball would be better?
-
The extra keys on the left side are barely useful.
It’s not a big deal, maybe I can find some usage for them, but to me having barely useful keys feels wrong.
-
There are more extra features I feel an ultimate keyboard should have.
The keyboard I’ve built is nice… But it’s still just a normal keyboard with a trackball. Maybe a vibration sensor, a display, or even some LEDs? A smart knob with software-configurable endstops and detents would really add some weight to the moniker of an ultimate keyboard.
Next steps
It’s hard to know how good the keyboard is before I’ve put it through extensive use, and to do that I need to settle on a keyboard layout for the keyboard. I’ve already designed a layout for a 34-key keyboard that should be fairly straightforward to adapt but I still need to figure out how to add mouse keys and what to do with the “extra” keys on the left-hand side.
Check out The current Cybershard layout for how the keyboard layout is coming along.