MoErgo Glove80 programmable ergonomic keyboard

Suraj N. Kurapati


NOTE: Check out the interactive layer map for this keyboard below!
DEMO: Watch this video of me typing on this keyboard at 100+ WPM.

Photograph of my MoErgo Glove80 keyboard.

Is that… Batman’s keyboard?! ๐Ÿฆ‡ No, but it sure makes me feel like him: sitting in the turquoise glow of the Batcomputer, deep within the quiet solitude of the Batcave, saving the world with this ergonomic marvel!

This is my MoErgo Glove80 keyboard: a split, columnar, contoured, and wireless ergonomic marvel that has become my “endgame” choice for the foreseeable future. Previously, I used an Ergohaven Remnant keyboard for 6 months, a Dactyl Manuform 5x6 keyboard for a year, an ErgoDox EZ keyboard for 6 years, and a Kinesis Advantage keyboard for 11 years prior — all on the quest for better ergonomics.

  1. Review
    1. Impression
      1. Adaptation
        1. Comparison
        2. Layers
          1. Lower layer
            1. Typing layer
              1. Gaming layer
              2. Cursor layer
                1. Mouse usage
                  1. Arrow keys
                    1. Select & edit
                    2. Number layer
                      1. Date & time
                        1. Arithmetic
                          1. Prefix signs
                            1. Inequalities
                              1. Vim navigation
                              2. Function layer
                                1. Symbol layer
                                  1. Spacegrams
                                    1. Base layer affinity
                                      1. Vim editor shortcuts
                                        1. Pinching bigrams
                                          1. Vertical bigrams
                                            1. Adjacent bigrams
                                              1. Inward rolling bigrams
                                                1. Outward rolling bigrams
                                                2. Mouse layer
                                                  1. System layer
                                                    1. Emoji layer
                                                      1. emoji.yaml
                                                      2. World layer
                                                        1. world.yaml
                                                      3. Firmware
                                                        1. Fine-tuning the timing
                                                          1. Mirroring horizontally

                                                          Review

                                                          Embodying a thoughtful, humanistic design that is forgiving (accommodating a broad range of hand sizes and shapes), inviting (like a lounge chair whose intuitive shape beckons weary hands to perch down for some well deserved rest), and futuristic (like some advanced Space Age technology that Batman would use), the Glove80 provides a comfortable, efficient, and delightful typing experience.

                                                          For me, the Glove80 is like a dream come true because it brings together the best features of all the keyboards I’ve used so far on my quest for ergonomics:

                                                          Ergonomic keyboard Duration of use Split design? Contoured keywells? Columnar layout? Programmable firmware? Low height? Wireless capable?
                                                          MoErgo Glove80 ๐Ÿ† 6 months โœ… โœ… โœ… โœ… โœ… โœ…
                                                          Ergohaven Remnant 6 months โœ… โœ… โœ… โœ… โœ… โŒ
                                                          Dactyl Manuform 5x6 1 year โœ… โœ… โœ… โœ… โŒ โŒ
                                                          ZSA ErgoDox EZ 6 years โœ… โŒ โœ… โœ… โœ… โŒ
                                                          Kinesis Advantage 11 years ๐ŸŒ” โœ… โœ… ๐ŸŒ’ โŒ โŒ
                                                          Goldtouch Adjustable 1 year ๐ŸŒ“ โŒ โŒ โŒ โœ… โŒ
                                                          Microsoft Natural Elite 2 years ๐ŸŒ’ โŒ โŒ โŒ โœ… โŒ

                                                          I especially revered the Kinesis Advantage for its unconventional yet ingenious design, but I often wished it were split so that I could separate the keywells at shoulder width apart for a more relaxed, natural stance. Thus I embarked on a split keyboard journey, sacrificing keywells in favor of a split design. ๐Ÿ’” Fortunately, my original wish has now been granted: the Glove80 delivers it all!

                                                          Impression

                                                          My first impressions of this keyboard, after connecting it to my computer, were:

                                                          Adaptation

                                                          On my first day using the Glove80, coming from an Ergohaven Remnant keyboard, I felt right at home: achieving 100 WPM on MonkeyType, as shown below. And this speed is with home row mods, disambiguated by ZMK’s global-quick-tap feature! ๐Ÿš€

                                                          First day on Glove80: achieved 100 WPM on MonkeyType!

                                                          Key travel is definitely less pronounced on the Glove80’s special-order Choc v1 Red key switches, which allows for a more rapid inter-key typing speed, and the tighter key spacing afforded by Choc switches (compared to MX switches) along with the Glove80’s ergonomic curvature are helping to minimize finger travel around the keyboard. With more practice (just normal usage over time), I feel confident that I can increase my touch-typing speed even further on the Glove80.

                                                          Porting my QMK keymap from the Remnant over to ZMK on the Glove80 was challenging, mainly because my home row mods disambiguation logic is so complex. Notably, I worried whether I would be able to translate all the nuances of my QMK patch (which took over 6 months of occasional development to master) to ZMK, but thankfully it was significantly simpler than what I had imagined: without modifying ZMK source code or writing C extensions, I was able to port nearly all of that monstrous sophistication using declarative snippets of custom behaviors!

                                                          Comparison

                                                          Bird's eye view of my Glove80 and Remnant keyboards.

                                                          In terms of quality, the Glove80 is strong yet light: hallmarks of high-end polycarbonate material, which is also used to make optical lenses in eyeglasses. This stands opposite to the Remnant’s light yet fragile 3D-printed construction, which requires careful handling since it can be damaged easily, say, if dropped.

                                                          In terms of height, the Glove80 achieves a lower height profile due to its center of gravity (its keywell nearly touches the desk!) in combination with low-profile Choc switches and keycaps. Even at its tallest point, the Glove80 still stands below the height of the Remnant’s top-inner corner key (number 5).

                                                          Outer profile: looking from the left side toward the center.

                                                          ๐Ÿ‘† Outer profile: looking from the left side toward the center.

                                                          Inner profile: looking from the center toward the left side.

                                                          ๐Ÿ‘† Inner profile: looking from the center toward the left side.

                                                          Front profile: looking from the palm rest toward the back.

                                                          ๐Ÿ‘† Front profile: looking from the palm rest toward the back.

                                                          Back profile: looking from the back toward the palm rest.

                                                          ๐Ÿ‘† Back profile: looking from the back toward the palm rest.

                                                          In terms of weight, the Glove80 feels much lighter than the Remnant even though it weighs negligibly more: using a kitchen scale, I observed that the right half of the Glove80 (with palm rest) weighed 9.7oz / 275g whereas the Remnant’s (without palm rest) weighed 9.6oz / 272g. However, the Glove80’s weight is more evenly distributed over its surface so it doesn’t feel as heavy when you lift it up; whereas the Remnant’s shape is more condensed, bulkier, and harder to grasp.

                                                          In terms of mobility, the Glove80’s two halves always communicate wirelessly whereas the left half can be wired, wireless, or both (since it supports connecting to 5 devices simultaneously: 4 Bluetooth devices and 1 USB device). This allows both halves, especially the right half, to be repositioned at will. For instance, I sometimes need more writing space on my desk to take notes or doodle, so I simply lift the right half up & away to make room for my notebook. And when I’m finished, I simply move the right half back into position: setting it down wherever I find is comfortable for me, approximately at shoulder width. This is in stark contrast to the Remnant, which is always and only wired to USB. As a result, I’m hesitant to reposition the Remnant’s two halves freely because that may disturb their perfect symmetry, which is tedious to restore accurately.

                                                          In terms of compatibility, the Glove80 works flawlessly with wireless Bluetooth devices, like smartphones, as well as wired USB devices, like laptops. The former is especially remarkable: I connected my Glove80 to my smartphone as an experiment to type in a passphrase (why struggle with a virtual on-screen keyboard when you’ve got the real thing Bluetooth-enabled?) and I was amazed! ๐Ÿคฉ This has unlocked a massive productivity boost for the otherwise inefficient smartphone user interface. It’s quite the game changer: Glove80 for the win! โค๏ธโ€๐Ÿ”ฅ

                                                          In terms of firmware, the Glove80’s online layout editor makes it easy to customize the keymap and share it with others: there is a free-form text box for notes alongside a “Custom Defined Behaviors” box for ZMK configuration snippets, which lets you implement advanced features such as home row mods disambiguation. And once you’ve built and downloaded your keymap from the online layout editor, flashing the resulting *.uf2 file is a simple drag & drop operation after entering bootloader mode, which has a robust hardware fail-safe of holding particular keys while powering on the keyboard, as documented in the user guide.

                                                          In terms of documentation, the Glove80 comes with a comprehensive user guide that covers everything from ergonomics to soldering; gentle introductory emails when your order is being prepared for shipment; and a helpful Discord community so thoughtful and engaging that I look forward to participating in it every day.

                                                          In terms of support, the Glove80’s inventors are active participants in the Discord community, and they’re reachable directly by email and other means too; their professional demeanor and responsiveness is often applauded by customers. This is significantly better than the Remnant’s support, where language and cultural differences sometimes complicate communication and problem resolution.

                                                          Overall, the Glove80 is a phenomenal upgrade over the Remnant and all other ergonomic keyboards I’ve used before. It’s like a dream come true: I love it! ๐Ÿ‘Œ

                                                          Layers

                                                          The keyboard boots up into the following default “base” layer when powered on. When held, the purple keys in the thumb clusters activate the subsequent layers according to the legendary Miryoku’s 6-layer design with 3-key thumb activation.

                                                          Download: This layer map is also available as a printable PDF document. Interactive: Hover your mouse over the purple keys to see each layer!

                                                          The keys are arranged in my variation of Arno Klein’s Engram 2.0 layout and they’re imbued with the legendary Miryoku home row mods tamed with enhancements.

                                                          Going beyond Miryoku, I have added a custom layer locking feature whereby a Miryoku layer can be set to remain active even after you release its thumb key: while holding the Lower key, simply tap your desired Miryoku layer’s thumb key. To unlock the layer, tap the Lower key again on the same hand as its thumb key.

                                                          Lower layer

                                                          This layer provides a layer locking facility for the other layers in the keymap:

                                                          Typing layer

                                                          This layer temporarily disables Miryoku layers and home row modifiers so that you can focus on typing, without the risk of activating them unintentionally. It’s meant for practicing your touch-typing skills and is helpful for newcomers.

                                                          Gaming layer

                                                          This layer repositions the WASD cluster to better fit the Glove80’s ergonomic curvature while retaining the conventional placement of the pinky finger’s keys. Consequently, the index finger’s home position is shifted down by one row (on the “D” key) instead of the home row (on the “E” key), as marked in the diagram.

                                                          It also replicates the first column of the right half of the keyboard on the left half because it’s common practice in games to place hotkeys on both central columns as they assume close proximity on conventional row-staggered keyboards. For example, notice how the home row ends with F and so the G wraps back around.

                                                          Similarly, it also replicates nearby hotkeys (such as M for map) on the top.

                                                          Cursor layer

                                                          Mouse usage

                                                          Many keys from the right half are mirrored on the left half for more convenient right-handed mouse usage. For example, you can select and drag with your right hand on the mouse while cutting, copying, and pasting with your left hand on the keyboard. Similarly, you can undo & redo and run the text selection macros too.

                                                          Arrow keys

                                                          The up/down arrow keys on the right-hand home row diverge from Vim’s HJKL order to avoid double-duty on the index finger for HJ (left & down directions) so that each home row finger is only responsible for a single direction; and also because it feels more natural to follow the inward-rising curve of the keyboard’s contoured keywell, which elevates the thumb above the pinky finger and, similarly, the middle finger (up arrow) above the ring finger (down arrow).

                                                          This is a longstanding preference that I formed 17 years ago, in my early days of using the Kinesis Advantage with the Dvorak layout, whose lack of HJKL provided the freedom to reimagine the arrangement of arrow keys on the home row.

                                                          Select & edit

                                                          Editing (index finger) and selection (thumb cluster) keys line the inner wall. This opposition allows for pinching, where selections can be followed by edits. For example, to copy everything, I would first tap the “Select all” key with my thumb and then pinch slightly inward to tap the “Copy” key with my index finger.

                                                          The copy and paste keys are stacked vertically, in that order, to allow the index finger to rake down upon them in a natural curling motion toward the palm. This order is also logical, since pasting requires something to be copied first.

                                                          The select & extend word/line keys on the thumb cluster are ZMK macros that approximate Pascal Getreuer’s word selection QMK macro, which automates common selection tasks that require holding down Control and Shift with the arrow keys:

                                                          Video demonstration of Pascal Getreuer's word selection QMK macro

                                                          These keys can be combined with the Shift modifier to reverse their direction. In some apps, the select word key cycles to the next successive word each time!

                                                          Number layer

                                                          A 3x3 numeric keypad (using the standard 10-key layout) occupies the home block. The period and comma keys are positioned near the zero key on the thumb cluster.

                                                          Date & time

                                                          The slash and minus keys are positioned for MM/DD and YYYY-MM-DD date entry. Similarly, the colon key is positioned above them for HH:MM:SS time stamp entry.

                                                          Arithmetic

                                                          Common arithmetic operators pair along the sides of the 3x3 numeric keypad.

                                                          Prefix signs

                                                          Signs that commonly prefix numbers line the top of the 3x3 numeric keypad.

                                                          Inequalities

                                                          Comparison operators are positioned along the perimeter of the home block.

                                                          Vim navigation

                                                          Vim navigation operators (prefixed by a number N) fill the rightmost column:

                                                          Function layer

                                                          The function keys are arranged in the same 10-key layout as the Number layer’s 3x3 numeric keypad so that you can develop common muscle memory for both layers. The remaining F10-F12 keys wrap around the home block because they’re found in shortcuts such as BIOS save/quit, fullscreen toggle, and devtools, respectively.

                                                          Symbol layer

                                                          This is the crown jewel of my keyboard’s configuration: an entire layer dedicated to the entry of symbols that are essential for computer programming. It’s the result of several hundreds of layout iterations over the last 9+ years.

                                                          Video: Tour of my Symbol layer, with examples in Vim.

                                                          Click to watch video

                                                          ๐Ÿ‘‰ Red quotes. Green arrows. Blue groups. Purple flips. Yellow Vim.

                                                          Overall, the guiding principle for this layer’s design is to favor inward rolls.

                                                          Next, I also consider the ergonomics (i.e. what feels comfortable) of the keyboard. For example, inward rolls starting with the pinky finger are easier to perform from the home and upper rows when compared to the lower and bottom rows. This is why I’ve placed ! on the upper row instead of the lower row, where ~ is.

                                                          Finally, I also consider clustering, balance, and base layer affinity. For example, ! on the upper pinky rolls into () for grouped negation !() and also into ? for interrobang !?. Similarly, () rolls into ? for optional group capture ()? in regular expressions. In this way, I feel that !, (), and ? are balanced.

                                                          Spacegrams

                                                          Video: Tour of my Spacegram operators, at timestamp 10:14.

                                                          Click to watch video

                                                          When typing two symbols separated by a space, I have to first let go of the symbol layer access key (also on spacebar) after the first symbol and then tap space and finally reactivate the symbol layer for the second symbol. ๐Ÿ˜ฉ I learned a few months ago that this anti-pattern is called a “spacegram” and I’ve already made some progress to minimize its disruption by configuring the spacebar separately from the rest of the Miryoku thumb layer activation keys, but that wasn’t enough! ๐Ÿ˜ค

                                                          So I came up with the following revised design for the Symbol layer that puts spacebar (as well as backspace and enter) on the right hand. ๐Ÿฅผโœจ This way, we don’t need to exit the Symbol layer to type spaces, newlines, or even to correct mistakes! ๐Ÿคฏ It has eliminated the delicately timed dance around the spacebar for me when programming now. ๐Ÿ˜Ž๐Ÿ‘Œ

                                                          Click to watch video Demonstration of typing Rust code using Spacegram operators.

                                                          Base layer affinity

                                                          Vim editor shortcuts

                                                          Pinching bigrams

                                                          Vertical bigrams

                                                          Adjacent bigrams

                                                          Inward rolling bigrams

                                                          Outward rolling bigrams

                                                          Mouse layer

                                                          Movement keys are located centrally in the home block, surrounded by wheel keys.

                                                          Mousewheel down/up keys are also placed on the home block, specifically on the same keys as J/K (down/up in Vim) on the base layer for muscle memory reuse.

                                                          System layer

                                                          Keys for controlling RGB matrix settings line the central rows of home block. Keys for power and the traditional print->pause triplet line the thumb cluster.

                                                          Emoji layer

                                                          Consistent with the cardinal directions of the arrow keys on the cursor layer, these Emojis are organized into logical groups of 4 (and more) within each row.

                                                          Customizable preset selections for skin tone, hair style, and gender sign are arranged on the home row so you can inward-roll them together in one swoop. See the #define EMOJI_*_PRESET settings in the ZMK snippet for more information.

                                                          Here are some examples (as illustrated interactively in the diagram above):

                                                          emoji.yaml

                                                          This file in the source repository for this keymap’s firmware defines the above. You can customize this file to suit your needs, as it’s already Unicode enabled.

                                                          # modifiers
                                                          zwj: "\u200d"
                                                          male_sign: "โ™‚๏ธ"
                                                          female_sign: "โ™€๏ธ"
                                                          right_arrow: "โžก๏ธ"
                                                          left_arrow: "โฌ…๏ธ"
                                                          
                                                          # skin tones
                                                          light_skin_tone:        "๐Ÿป"
                                                          medium_light_skin_tone: "๐Ÿผ"
                                                          medium_skin_tone:       "๐Ÿฝ"
                                                          medium_dark_skin_tone:  "๐Ÿพ"
                                                          dark_skin_tone:         "๐Ÿฟ"
                                                          
                                                          # family & ages
                                                          baby_bottle: "๐Ÿผ"
                                                          baby: "๐Ÿ‘ถ"
                                                          boy: "๐Ÿ‘ฆ"
                                                          girl: "๐Ÿ‘ง"
                                                          man: "๐Ÿ‘จ"
                                                          woman: "๐Ÿ‘ฉ"
                                                          old_man: "๐Ÿ‘ด"
                                                          old_woman: "๐Ÿ‘ต"
                                                          
                                                          # hair styles
                                                          white_hair: "๐Ÿฆณ"
                                                          curly_hair: "๐Ÿฆฑ"
                                                          red_hair: "๐Ÿฆฐ"
                                                          bald: "๐Ÿฆฒ"
                                                          
                                                          # function row
                                                          new_moon: "๐ŸŒ‘"
                                                          waxing_crescent_moon: "๐ŸŒ’"
                                                          first_quarter_moon: "๐ŸŒ“"
                                                          waxing_gibbous_moon: "๐ŸŒ”"
                                                          full_moon: "๐ŸŒ•"
                                                          
                                                          # number row
                                                          tada: "๐ŸŽ‰"
                                                          fire: "๐Ÿ”ฅ"
                                                          heart: "๏ธโค๏ธ"
                                                          muscle: "๐Ÿ’ช"
                                                          person_climbing: "๐Ÿง—"
                                                          rocket: "๐Ÿš€"
                                                          
                                                          # upper row
                                                          sunglasses: "๐Ÿ˜Ž"
                                                          star_struck: "๐Ÿคฉ"
                                                          joy: "๐Ÿ˜‚"
                                                          cold_sweat: "๐Ÿ˜ฐ"
                                                          scream: "๐Ÿ˜ฑ"
                                                          exploding_head: "๐Ÿคฏ"
                                                          
                                                          # home row
                                                          snap_fingers: "๐Ÿซฐ"
                                                          ok_hand: "๐Ÿ‘Œ"
                                                          pray: "๐Ÿ™"
                                                          sweat_smile: "๐Ÿ˜…"
                                                          disappointed: "๐Ÿ˜ž"
                                                          thinking: "๐Ÿค”"
                                                          
                                                          # lower row
                                                          person_tipping_hand: "๐Ÿ’"
                                                          person_gesturing_ok: "๐Ÿ™†"
                                                          person_bowing: "๐Ÿ™‡"
                                                          person_raising_hand: "๐Ÿ™‹"
                                                          person_gesturing_no: "๐Ÿ™…"
                                                          person_shrugging: "๐Ÿคท"
                                                          
                                                          # bottom row
                                                          checkoff: "โœ…"
                                                          100: "๐Ÿ’ฏ"
                                                          warning: "โš ๏ธ"
                                                          x: "โŒ"
                                                          question: "โ“"
                                                          
                                                          # thumb cluster upper arc
                                                          astronaut:  "๐Ÿง‘โ€๐Ÿš€"
                                                          nerd: "๐Ÿค“"
                                                          sparkles: "โœจ"
                                                          
                                                          # thumb cluster lower arc
                                                          raised_hands: "๐Ÿ™Œ"
                                                          point_up: "โ˜๏ธ"
                                                          thumbs_up: "๐Ÿ‘"
                                                          

                                                          World layer

                                                          This layer overlays Latin characters atop related letters in the vowel cluster. Modifiers along the right hand’s home row act as sub-layers, working together to target the desired accented character from each of the overloaded vowel letters.

                                                          legend
                                                          LALT RSFT RALT
                                                          LCTL base RCTL

                                                          For example, tapping Y produces different characters depending on the modifiers:

                                                          Letter Modifier Shift? Output
                                                          Y โŒ โŒ รฝ
                                                          Y โŒ โœ… ร
                                                          Y LCTL โŒ รฟ
                                                          Y LCTL โœ… ลธ

                                                          world.yaml

                                                          This file in the source repository for this keymap’s firmware defines the above. You can customize this file to suit your needs, as it’s already Unicode enabled.

                                                          precedence: [LSFT, LALT, RALT, LCTL, RCTL, RSFT] # latter overrides former: RSFT overrides all
                                                          
                                                          transforms:
                                                            C: { base: cedilla                                                                         }
                                                            I: { base: acute, LCTL: diaeresis, RCTL: circumflex, RSFT: grave                           }
                                                            E: { base: acute, LCTL: diaeresis, RCTL: circumflex, RSFT: grave,              RALT: ae    }
                                                            A: { base: acute, LCTL: diaeresis, RCTL: circumflex, RSFT: grave, LALT: tilde, RALT: ring  }
                                                            Y: { base: acute, LCTL: diaeresis                                                          }
                                                            O: { base: acute, LCTL: diaeresis, RCTL: circumflex, RSFT: grave, LALT: tilde, RALT: slash }
                                                            U: { base: acute, LCTL: diaeresis, RCTL: circumflex, RSFT: grave                           }
                                                            N: { base: tilde                                                                           }
                                                          
                                                          characters:
                                                            C:
                                                              cedilla:    { lower: "รง", upper: "ร‡" }
                                                            I:
                                                              acute:      { lower: "รญ", upper: "ร" }
                                                              diaeresis:  { lower: "รฏ", upper: "ร" }
                                                              circumflex: { lower: "รฎ", upper: "รŽ" }
                                                              grave:      { lower: "รฌ", upper: "รŒ" }
                                                            E:
                                                              acute:      { lower: "รฉ", upper: "ร‰" }
                                                              diaeresis:  { lower: "รซ", upper: "ร‹" }
                                                              circumflex: { lower: "รช", upper: "รŠ" }
                                                              grave:      { lower: "รจ", upper: "รˆ" }
                                                              ae:         { lower: "รฆ", upper: "ร†" }
                                                            A:
                                                              acute:      { lower: "รก", upper: "ร" }
                                                              diaeresis:  { lower: "รค", upper: "ร„" }
                                                              circumflex: { lower: "รข", upper: "ร‚" }
                                                              grave:      { lower: "ร ", upper: "ร€" }
                                                              tilde:      { lower: "รฃ", upper: "รƒ" }
                                                              ring:       { lower: "รฅ", upper: "ร…" }
                                                            Y:
                                                              acute:      { lower: "รฝ", upper: "ร" }
                                                              diaeresis:  { lower: "รฟ", upper: "ลธ" }
                                                            O:
                                                              acute:      { lower: "รณ", upper: "ร“" }
                                                              diaeresis:  { lower: "รถ", upper: "ร–" }
                                                              circumflex: { lower: "รด", upper: "ร”" }
                                                              grave:      { lower: "รฒ", upper: "ร’" }
                                                              tilde:      { lower: "รต", upper: "ร•" }
                                                              slash:      { lower: "รธ", upper: "ร˜" }
                                                            U:
                                                              acute:      { lower: "รบ", upper: "รš" }
                                                              diaeresis:  { lower: "รผ", upper: "รœ" }
                                                              circumflex: { lower: "รป", upper: "ร›" }
                                                              grave:      { lower: "รน", upper: "ร™" }
                                                            N:
                                                              tilde:      { lower: "รฑ", upper: "ร‘" }
                                                          
                                                          codepoints:
                                                            degree_sign: "ยฐ"
                                                            section_sign: "ยง"
                                                            paragraph_sign: "ยถ"
                                                            o_ordinal: "ยบ"
                                                            a_ordinal: "ยช"
                                                            exclaim_left: "ยก"
                                                            question_left: "ยฟ"
                                                            quote_left: "ยซ"
                                                            quote_right: "ยป"
                                                          

                                                          Firmware

                                                          My keyboard’s entire firmware is available on GitHub and discussed on Discord.

                                                          Fine-tuning the timing

                                                          Activate the typing layer, launch the QMK Configurator’s testing tool, and then pretend to use home row mods. Note the timing & duration of keystrokes reported by the tool and use them to adjust the #define timing thresholds in my ZMK port.

                                                          Mirroring horizontally

                                                          To horizontally mirror a keymap’s physical layout in the Glove80 Layout Editor:

                                                          1. Activate the “Enable local config” option in the Glove80 Layout Editor’s settings panel under the “Experimental Settings” section.
                                                          2. Return to the editor and export your keymap to a JSON file by clicking on the “Download” button.
                                                          3. Paste the contents of the exported JSON file into your Web browser’s JavaScript console (found in the “Developer Tools” panel, typically activated by pressing Ctrl+F12) at the location indicated by the comment in the first line of the following code snippet.
                                                          4. Right-click the result, copy to clipboard, save to file, and upload into the Glove80 Layout Editor. ๐Ÿซฐ Presto! Everything should be mirrored now.
                                                          layout = /* paste contents of exported JSON file here */;
                                                          mirroring_transformation = {
                                                          //
                                                          // |------------------------|------------------------|
                                                          // | LEFT_HAND_KEYS         |        RIGHT_HAND_KEYS |
                                                          // |                        |                        |
                                                          // |  0  1  2  3  4         |          5  6  7  8  9 |
                                                          // | 10 11 12 13 14 15      |      16 17 18 19 20 21 |
                                                          // | 22 23 24 25 26 27      |      28 29 30 31 32 33 |
                                                          // | 34 35 36 37 38 39      |      40 41 42 43 44 45 |
                                                          // | 46 47 48 49 50 51      |      58 59 60 61 62 63 |
                                                          // | 64 65 66 67 68         |         75 76 77 78 79 |
                                                          // |                69 52   |   57 74                |
                                                          // |                 70 53  |  56 73                 |
                                                          // |                  71 54 | 55 72                  |
                                                          // |------------------------|------------------------|
                                                          // | LEFT_HAND_MIRRORED     |    RIGHT_HAND_MIRRORED |
                                                          // |                        |                        |
                                                          // |  9  8  7  6  5         |          4  3  2  1  0 |
                                                          // | 21 20 19 18 17 16      |      15 14 13 12 11 10 |
                                                          // | 33 32 31 30 29 28      |      27 26 25 24 23 22 |
                                                          // | 45 44 43 42 41 40      |      39 38 37 36 35 34 |
                                                          // | 63 62 61 60 59 58      |      51 50 49 48 47 46 |
                                                          // | 79 78 77 76 75         |         68 67 66 65 64 |
                                                          // |                74 57   |   52 69                |
                                                          // |                 73 56  |  53 70                 |
                                                          // |                  71 54 | 55 72                  |
                                                          // |------------------------|------------------------|
                                                          //
                                                             0: 9,  1: 8,  2: 7,  3: 6,  4: 5,                              5: 4,  6: 3,  7: 2,  8: 1,  9: 0,
                                                            10:21, 11:20, 12:19, 13:18, 14:17, 15:16,               16:15, 17:14, 18:13, 19:12, 20:11, 21:10,
                                                            22:33, 23:32, 24:31, 25:30, 26:29, 27:28,               28:27, 29:26, 30:25, 31:24, 32:23, 33:22,
                                                            34:45, 35:44, 36:43, 37:42, 38:41, 39:40,               40:39, 41:38, 42:37, 43:36, 44:35, 45:34,
                                                            46:63, 47:62, 48:61, 49:60, 50:59, 51:58,               58:51, 59:50, 60:49, 61:48, 62:47, 63:46,
                                                            64:79, 65:78, 66:77, 67:76, 68:75,                             75:68, 76:67, 77:66, 78:65, 79:64,
                                                                                               69:74, 52:57, 57:52, 74:69,
                                                                                               70:73, 53:56, 56:53, 73:70,
                                                                                               71:72, 54:55, 55:54, 72:71,
                                                          };
                                                          mirrored_layers = layout["layers"].map((layer) => {
                                                            return layer.map((key,pos) => {
                                                              return layer[mirroring_transformation[pos]];
                                                            });
                                                          });
                                                          mirrored_layout = Object.assign({}, layout);
                                                          mirrored_layout["layers"] = mirrored_layers;
                                                          mirrored_layout; /* dumps to the console for copying */
                                                          

                                                          Updates