QMK Community Modules for custom RGB keyboard firmware.
This repository currently provides three modules:
| Module | Description |
|---|---|
key_colors |
Per-key, per-layer RGB colors with EEPROM persistence and split synchronization |
audio_visualizer |
Audio-reactive RGB relay using VialRGB direct-mode data |
layerhook |
Raw HID layer switching for the layerhook host app |
The modules are designed for Vial-QMK and are currently used by several custom keyboard projects.
Custom per-key and per-layer RGB colors with persistent storage and split-keyboard synchronization.
The module provides the RGB Matrix effect:
RGB_MATRIX_COMMUNITY_MODULE_key_colors
Features include:
- Per-key RGB colors
- Per-layer color configuration
- EEPROM persistence
- Split synchronization
- Blink colors
- Lock-state handling
- Browser-based WebGUI support
- Optional keybinding to return to the
key_colorsRGB mode
The WebGUI communicates with the firmware through HID.
key_colors persists DYNAMIC_KEYMAP_LAYER_COUNT × RGB_MATRIX_LED_COUNT × 7
bytes — on boards with many LEDs and/or layers this can be a sizeable chunk
of the available EEPROM. Whether it fits, and whether that's fixable, depends
on the keyboard's EEPROM driver:
| MCU / driver | Notes | |
|---|---|---|
| ✅ Tested | RP2040 (wear_leveling rp2040_flash) |
PandaKB Sofle RGB |
| ✅ Tested | STM32F401 (wear_leveling legacy) |
BCorne M57 |
| 🟡 Should work, untested | STM32F411 (wear_leveling legacy) |
Same driver family as F401 |
| 🟡 Should work, untested | STM32F0xx/F1xx/F3xx (wear_leveling embedded_flash / vendor) |
Flash-emulated, same expandable model |
| 🟡 Should work, untested | STM32L0xx/L1xx (vendor, true onboard EEPROM) | Erase can take up to 1s per 1kB used |
| 🟡 Should work, untested | External I2C/SPI EEPROM chip | Size set via EXTERNAL_EEPROM_BYTE_COUNT, easy to have plenty of room |
| AVR (atmega32u4/32u2, "Pro Micro" boards) | Real onboard EEPROM, fixed at ~1kB, not expandable via config — the classic Pro Micro/Elite-C boards this hits first | |
| ❌ Not usable | EEPROM_DRIVER = transient |
RAM-only, lost on every power cycle — defeats the point of key_colors' persistence entirely |
On any driver backed by wear-leveled flash (RP2040, STM32 non-L0/L1), a
"Dynamic keymaps are configured to use more EEPROM than is available" build
error can usually be fixed by raising WEAR_LEVELING_BACKING_SIZE to a
concrete number — see Firmware does not
fit below. On AVR's fixed onboard EEPROM there is no
such knob; if it doesn't fit, it doesn't fit.
An audio-reactive RGB relay for QMK/VialRGB.
The module receives RGB data from the host application and renders it through QMK's RGB Matrix system.
The module provides the RGB Matrix effect:
RGB_MATRIX_COMMUNITY_MODULE_audio_visualizer
It supports:
- VialRGB direct-mode colors
- Host-driven RGB updates
- Entry-wave startup animation
- Split synchronization
The audio analysis itself is performed on the host. The keyboard firmware receives the resulting data and handles the actual RGB rendering.
The host-side application is:
kolbenhans/audio-visualizer-gui
These modules use QMK's Community Modules mechanism.
The following examples assume a Vial-QMK checkout at:
~/projects/vial-qmk
cd ~/projects/vial-qmk
git submodule add \
https://github.com/kolbenhans/qmk-modules.git \
modules/kolbenhans
git submodule update --init --recursiveAfter installation, the relevant directory structure is:
vial-qmk/
├── modules/
│ └── kolbenhans/
│ ├── audio_visualizer/
│ ├── docs/
│ ├── hid_glue/
│ └── key_colors/
└── ...
Add the module identifier to the "modules" array in your keymap's keymap.json.
Most keymaps don't have one yet — if keyboards/<kb>/keymaps/<km>/keymap.json
doesn't exist, create it with just the snippet below, it's a complete, valid
keymap.json on its own.
For key_colors:
{
"modules": [
"kolbenhans/key_colors"
]
}For audio_visualizer:
{
"modules": [
"kolbenhans/audio_visualizer"
]
}If your keymap.json already contains a "modules" array, add the module to that existing array.
Do not create a second "modules" property. Duplicate JSON keys can result in one definition silently overriding the other.
Multiple modules can be enabled together:
{
"modules": [
"kolbenhans/key_colors",
"kolbenhans/audio_visualizer"
]
}Add the module to keymap.json:
{
"modules": [
"kolbenhans/key_colors"
]
}For split keyboards, older Vial-QMK versions may require the module's transaction IDs to be added manually:
#define SPLIT_TRANSACTION_IDS_USER \
KEY_COLORS_COLORS_DELTA, \
KEY_COLORS_BLINK_DELTA, \
KEY_COLORS_LOCK_FLAGS_DELTA, \
KEY_COLORS_COMMIT, \
KEY_COLORS_STARTUPIf your Vial-QMK version supports SPLIT_TRANSACTION_IDS_MODULE_*, this manual definition should not be necessary.
Optionally set key_colors as the default RGB Matrix mode:
#define RGB_MATRIX_DEFAULT_MODE \
RGB_MATRIX_COMMUNITY_MODULE_key_colorsIf the build fails because the firmware does not fit into the available wear-leveling storage, increase the backing size:
#define WEAR_LEVELING_BACKING_SIZE \
(WEAR_LEVELING_LOGICAL_SIZE * 2)Only add this when required by the target keyboard.
Make sure RGB Matrix is enabled:
RGB_MATRIX_ENABLE = yesThe HID glue source file is also required:
SRC += key_colors_hid.cCopy:
modules/kolbenhans/hid_glue/key_colors_hid.c
into the keymap directory, next to rules.mk.
The resulting structure should look like:
vial-qmk/
├── modules/
│ └── kolbenhans/
│ └── hid_glue/
│ └── key_colors_hid.c
│
└── keyboards/
└── exampleKeyboard/
└── keymaps/
└── exampleKeymap/
├── keymap.json
├── config.h
├── rules.mk
├── key_colors_hid.c
└── ...
A key can be assigned to return to the key_colors RGB Matrix mode. All of
the following goes in your keymap's keymap.c (create one if it doesn't
exist yet).
Include the module header, near the top of the file:
#include "key_colors.h"Define an unused custom keycode, also near the top:
#define KEYBIND_USER01 0x7E01Then handle it in process_record_user():
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case KEYBIND_USER01:
if (record->event.pressed) {
key_colors_on_mode_enter();
rgb_matrix_mode_noeeprom(
RGB_MATRIX_COMMUNITY_MODULE_key_colors
);
}
return false;
}
return true;
}Bind USER01 in your layout or later through Vial/Pipette.
If your keymap already has a process_record_user() function, add only the corresponding case rather than creating a second function.
Add the module to keymap.json:
{
"modules": [
"kolbenhans/audio_visualizer"
]
}For split keyboards, the module requires sufficient RPC buffer space for the RGB data.
Set:
#define RPC_M2S_BUFFER_SIZE \
<at least (RGB_MATRIX_LED_COUNT / 2) * 3>For example, if the keyboard has 60 LEDs:
#define RPC_M2S_BUFFER_SIZE 90The required size is based on the number of LEDs on one half:
(RGB_MATRIX_LED_COUNT / 2) × 3
The factor of 3 accounts for the RGB channels.
If your Vial-QMK version does not yet provide the module transaction-ID mechanism, you may also need:
#define SPLIT_TRANSACTION_IDS_USER \
AUDIO_VISUALIZER_SYNC_RGB_DIRECT, \
AUDIO_VISUALIZER_ENTRY_WAVE_STARTUPThis is only required when the build reports the corresponding transaction identifiers as undeclared.
The following features are required:
RGB_MATRIX_ENABLE = yes
VIALRGB_ENABLE = yesIf VIALRGB_ENABLE is already enabled by the keymap, do not add it a second time.
Add the HID glue source:
SRC += audio_visualizer_hid.cCopy:
modules/kolbenhans/hid_glue/audio_visualizer_hid.c
into the keymap directory next to rules.mk.
The resulting structure should look like:
vial-qmk/
├── modules/
│ └── kolbenhans/
│ └── hid_glue/
│ └── audio_visualizer_hid.c
│
└── keyboards/
└── exampleKeyboard/
└── keymaps/
└── exampleKeymap/
├── keymap.json
├── config.h
├── rules.mk
├── audio_visualizer_hid.c
└── ...
Lets the layerhook host app switch and query the active layer over Raw HID.
keymap.json:
{ "modules": ["kolbenhans/layerhook"] }rules.mk:
RAW_ENABLE = yes
SRC += layerhook_hid.cCopy modules/kolbenhans/hid_glue/layerhook_hid.c next to rules.mk.
Combined with key_colors and/or audio_visualizer, only list the module in keymap.json — their glue files pick it up automatically and layerhook_hid.c is not needed.
key_colors and audio_visualizer can be used together.
Enable both modules in keymap.json:
{
"modules": [
"kolbenhans/key_colors",
"kolbenhans/audio_visualizer"
]
}Copy both HID glue files into the keymap:
key_colors_hid.c
audio_visualizer_hid.c
No additional module-specific composition is required.
The modules automatically coexist and provide their respective RGB Matrix functionality.
A typical configuration therefore contains:
keymap.json
config.h
rules.mk
key_colors_hid.c
audio_visualizer_hid.c
The files in hid_glue/ are intentionally kept separate from the module directories.
They bridge the QMK module functionality with the keyboard/keymap-specific build environment.
Required by the key_colors WebGUI/HID interface.
Required by the host-side audio visualizer application.
Only needed when layerhook is used without the other two modules.
The files must be copied into the keymap directory and added to the build through rules.mk.
The key_colors module can be configured using the browser-based WebGUI:
The WebGUI communicates directly with compatible keyboards through WebHID.
It provides per-key and per-layer color configuration without requiring the firmware source to be modified.
The keyboard must expose the corresponding key_colors HID functionality.
For the required LED layout data, see:
docs/vial-json-rgb-matrix-layout.md
The host-side audio visualizer is provided by:
kolbenhans/audio-visualizer-gui
The architecture is:
┌──────────────────────────┐
│ Host Application │
│ │
│ Audio capture / FFT │
│ Peak detection │
│ RGB / palette handling │
└────────────┬─────────────┘
│
│ USB HID
▼
┌──────────────────────────┐
│ QMK Firmware │
│ │
│ audio_visualizer │
│ │ │
│ ▼ │
│ RGB Matrix │
└──────────────────────────┘
The host performs the audio analysis. The keyboard firmware receives the resulting data and renders the RGB effect.
Both the WebGUI and audio visualizer depend on correct RGB Matrix LED layout information.
See:
docs/vial-json-rgb-matrix-layout.md
The layout data describes the physical LED positions used by the host-side tools.
Incorrect or missing layout information can result in colors being assigned to the wrong keys or visualizer effects appearing in the wrong positions.
If the build reports errors involving:
KEY_COLORS_...
or:
AUDIO_VISUALIZER_...
your Vial-QMK version may not provide the corresponding module transaction-ID support.
For key_colors:
#define SPLIT_TRANSACTION_IDS_USER \
KEY_COLORS_COLORS_DELTA, \
KEY_COLORS_BLINK_DELTA, \
KEY_COLORS_LOCK_FLAGS_DELTA, \
KEY_COLORS_COMMIT, \
KEY_COLORS_STARTUPFor audio_visualizer:
#define SPLIT_TRANSACTION_IDS_USER \
AUDIO_VISUALIZER_SYNC_RGB_DIRECT, \
AUDIO_VISUALIZER_ENTRY_WAVE_STARTUPUse the definition appropriate to the module you are enabling.
If the build fails with:
static assertion failed: "Dynamic keymaps are configured to use more EEPROM than is available."
the keyboard only has a fixed amount of EEPROM for dynamic keymap data
(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR). The usual dominant consumer once
key_colors is added is key_colors itself — it reserves
DYNAMIC_KEYMAP_LAYER_COUNT × RGB_MATRIX_LED_COUNT × 7 bytes
(EECONFIG_KB_DATA_SIZE in the module's own config.h) ahead of Vial's
combos, key overrides, tap dance and macros, not alongside them — on a
board with many LEDs and/or layers this alone can already exceed the default
EEPROM budget, before any Vial feature counts even come into play.
Two ways forward:
- On a wear-leveling-backed MCU (see MCU / EEPROM compatibility above), grow the available EEPROM — see Firmware does not fit below.
- Otherwise, lower
DYNAMIC_KEYMAP_LAYER_COUNT(shrinks both the keymap andkey_colors' own block) — or, to keep all keymap layers but store colors for fewer of them, overrideEECONFIG_KB_DATA_SIZEdirectly to a smaller multiple. LoweringVIAL_COMBO_ENTRIES/VIAL_KEY_OVERRIDE_ENTRIES/VIAL_TAP_DANCE_ENTRIES/DYNAMIC_KEYMAP_MACRO_COUNTalso frees a little room, but rarely enough on its own oncekey_colorsis the main consumer. - If none of that gets it under budget,
key_colorsdoesn't fit on this board/firmware configuration as-is.
key_colors stores its configuration persistently, and needs the extra room
to already exist — on RP2040 and STM32 (any wear_leveling-backed driver),
the default backing store is usually too small once key_colors is added.
Set a concrete, larger value in your keymap's config.h:
#define WEAR_LEVELING_BACKING_SIZE 16384Don't write this as (WEAR_LEVELING_LOGICAL_SIZE * 2) unless your board's
config.h already defines WEAR_LEVELING_LOGICAL_SIZE to a concrete number
itself — on boards that don't (most don't), the platform's own default
derives WEAR_LEVELING_LOGICAL_SIZE from WEAR_LEVELING_BACKING_SIZE,
so the two definitions reference each other and the build fails with
'WEAR_LEVELING_LOGICAL_SIZE' undeclared / "expression in static assertion
is not an integer" instead.
The exact number needed depends on the keyboard's EEPROM/wear-leveling configuration (see the table above). As a rough estimate:
key_colors_bytes = DYNAMIC_KEYMAP_LAYER_COUNT × RGB_MATRIX_LED_COUNT × 7
needed_logical = key_colors_bytes + 2000 (headroom: keymap matrix,
Vial's own combos/overrides/
tap dance/macros, eeconfig)
rounded up to the next multiple of 4096 (flash sector
size on most MCUs)
WEAR_LEVELING_LOGICAL_SIZE = needed_logical
WEAR_LEVELING_BACKING_SIZE = needed_logical × 2
Example: 8 layers × 72 LEDs → 8×72×7 = 4032, +2000 = 6032, rounded up to
8192 → WEAR_LEVELING_BACKING_SIZE = 16384. This is only a starting
estimate, not a guarantee — if the build still doesn't fit, go one step
further (double needed_logical again). This has no effect on AVR's fixed
onboard EEPROM; see MCU / EEPROM compatibility
above.
Check:
#define RPC_M2S_BUFFER_SIZE \
<at least (RGB_MATRIX_LED_COUNT / 2) * 3>The buffer must be large enough to transport the RGB data for one half of the keyboard.
Check:
- The keyboard firmware contains
key_colors. key_colors_hid.cis present in the keymap.- The HID interface is compiled into the firmware.
- The browser supports WebHID.
- The keyboard is connected directly via USB.
These modules are currently used in several custom keyboard projects:
They form the firmware-side building blocks for the per-key RGB and audio-reactive RGB functionality used by these projects.
qmk-modules/
├── audio_visualizer/
├── docs/
│ └── vial-json-rgb-matrix-layout.md
├── hid_glue/
│ ├── audio_visualizer_hid.c
│ ├── key_colors_hid.c
│ └── layerhook_hid.c
├── key_colors/
└── layerhook/
The modules are intended for Vial-QMK with Community Modules support.
Some configuration details depend on the Vial-QMK version, particularly the split transaction-ID mechanism.
For older Vial-QMK versions, the SPLIT_TRANSACTION_IDS_USER definitions described above may be required.
See LICENSE for license information.
