Vial firmware and GUI support for Keychron keyboards.
This project brings full Vial support to Keychron keyboards, including advanced features like Keychron-specific settings, RGB control, and Hall Effect (HE) analog configuration.
| Resource | Description |
|---|---|
| Vial Web (Keychron Edition) | Browser-based configurator |
| Pipette Desktop (Alternative) | Modern Electron-based configurator (Recommended) |
| vial-gui Desktop App | Legacy Python-based desktop configurator |
| vial-qmk Firmware | QMK firmware with Vial + Keychron support |
| Keyboard Definitions | Vial keymaps |
Fork of vial-kb/vial-qmk with Keychron keyboard ports.
| Branch | Base | Status | Description |
|---|---|---|---|
vial-keychron |
wls_2025q1 | Legacy | Older QMK base. Does not fully support the custom GUI. |
vial-updated-keychron |
2025q3 | Active | Newer QMK base. Fully supports the custom GUI. |
Note: Not all keyboards have been ported to
vial-updated-keychronyet. Keyboards still only available onvial-keychronare marked accordingly.
Fork of darakuneko/pipette-desktop with a refined UI and built-in Pipette Hub for sharing layouts.
| Branch | Description |
|---|---|
vial-keychron |
Active — Main repository with Keychron support |
Fork of vial-kb/vial-gui with Keychron-specific configuration tabs.
| Branch | Description |
|---|---|
vial-keychron |
Adds Keychron settings tabs |
Fork of vial-kb/vial-web configured to use the Keychron-enabled GUI.
| URL | Description |
|---|---|
| vial.tymon3310.dev | Live deployment |
All keyboards below have Vial support with full keymap editing. Keychron-specific features (RGB, settings) require the custom GUI.
| Keyboard | Layouts | Branch |
|---|---|---|
| Q0 | base, plus | vial-updated-keychron |
| Q1v1 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| Q1v2 | ansi, ansi_encoder, iso, iso_encoder, jis, jis_encoder | vial-updated-keychron |
| Q2 | ansi, ansi_encoder, iso, iso_encoder, jis, jis_encoder | vial-updated-keychron |
| Q3 | ansi, ansi_encoder, iso, iso_encoder, jis, jis_encoder | vial-updated-keychron |
| Q4 | ansi, iso | vial-updated-keychron |
| Q5 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| Q6 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| Q7 | ansi, iso | vial-updated-keychron |
| Q8 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| Q9 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| Q9 PLUS | ansi_encoder | vial-updated-keychron |
| Q10 | ansi_encoder, iso_encoder | vial-updated-keychron |
| Q11 | ansi_encoder, iso_encoder | vial-updated-keychron |
| Q12 | ansi_encoder, iso_encoder | vial-updated-keychron |
| Q60 | ansi | vial-updated-keychron |
| Q65 | ansi_encoder | vial-updated-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| Q1 PRO | ansi_knob, iso_knob, jis_encoder | vial-keychron |
| Q2 PRO | ansi_encoder, iso_encoder | vial-keychron |
| Q3 PRO | ansi_encoder, ansi_encoder_se, iso_encoder, iso_encoder_se, jis_encoder_se | vial-keychron |
| Q4 PRO | ansi, iso | vial-keychron |
| Q5 PRO | ansi_encoder, iso_encoder | vial-keychron |
| Q6 PRO | ansi_encoder, iso_encoder | vial-keychron |
| Q8 PRO | ansi_encoder, iso_encoder | vial-keychron |
| Q10 PRO | ansi_encoder, iso_encoder | vial-keychron |
| Q13 PRO | ansi_encoder, iso_encoder | vial-keychron |
| Q14 PRO | ansi_encoder, iso_encoder | vial-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| Q0 MAX | encoder | vial-keychron |
| Q1 MAX | ansi_encoder, iso_encoder, jis_encoder | vial-keychron |
| Q2 MAX | ansi_encoder, iso_encoder | vial-keychron |
| Q3 MAX | ansi_encoder, iso_encoder | vial-keychron |
| Q5 MAX | ansi_encoder, iso_encoder | vial-keychron |
| Q6 MAX | ansi_encoder, iso_encoder | vial-keychron |
| Q8 MAX | ansi_encoder | vial-keychron |
| Q10 MAX | ansi_encoder, iso_encoder | vial-keychron |
| Q12 MAX | ansi_encoder | vial-keychron |
| Q13 MAX | ansi_encoder | vial-keychron |
| Q14 MAX | ansi_encoder | vial-keychron |
| Q15 MAX | ansi_encoder | vial-keychron |
| Q60 MAX | ansi | vial-keychron |
| Q65 MAX | ansi_encoder | vial-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| Q1 HE | ansi_encoder, iso_encoder, jis_encoder | vial-updated-keychron |
| Q2 HE | ansi_encoder | vial-updated-keychron |
| Q3 HE | ansi_encoder, iso_encoder, jis_encoder | vial-updated-keychron |
| Q4 HE | ansi | vial-updated-keychron |
| Q5 HE | ansi_encoder, iso_encoder, jis_encoder | vial-updated-keychron |
| Q6 HE | ansi_encoder, iso_encoder, jis_encoder | vial-updated-keychron |
| Q12 HE | ansi_encoder, iso_encoder | vial-updated-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| V1 | ansi, ansi_encoder, iso, iso_encoder, jis, jis_encoder | vial-updated-keychron |
| V2 | ansi, ansi_encoder, iso, iso_encoder, jis, jis_encoder | vial-updated-keychron |
| V3 | ansi, ansi_encoder, iso, iso_encoder, jis, jis_encoder | vial-updated-keychron |
| V4 | ansi, iso | vial-updated-keychron |
| V5 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| V6 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| V7 | ansi, iso | vial-updated-keychron |
| V8 | ansi, ansi_encoder, iso, iso_encoder | vial-updated-keychron |
| V10 | ansi_encoder, iso_encoder | vial-updated-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| V1 MAX | ansi_encoder, iso_encoder, jis_encoder | vial-updated-keychron |
| V2 MAX | ansi_encoder, iso_encoder | vial-keychron |
| V3 MAX | ansi_encoder, iso_encoder | vial-keychron |
| V4 MAX | ansi | vial-keychron |
| V5 MAX | ansi_encoder, iso_encoder, jis_encoder | vial-updated-keychron |
| V6 MAX | ansi_encoder, iso_encoder, jis_encoder | vial-updated-keychron |
| V8 MAX | ansi_encoder | vial-keychron |
| V10 MAX | ansi_encoder | vial-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| K1 PRO | iso/rgb | vial-keychron |
| K2 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K3 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K4 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white | vial-keychron |
| K5 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K6 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb | vial-keychron |
| K7 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white | vial-keychron |
| K8 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-updated-keychron |
| K9 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K10 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K11 PRO | ansi/rgb, ansi/white, ansi_encoder/rgb, ansi_encoder/white | vial-keychron |
| K12 PRO | ansi/rgb, ansi/white | vial-keychron |
| K13 PRO | ansi/rgb, ansi/white, iso/rgb, iso/white | vial-keychron |
| K14 PRO | ansi/rgb, ansi/white | vial-keychron |
| K15 PRO | ansi_encoder/rgb, ansi_encoder/white | vial-keychron |
| K17 PRO | ansi_encoder/rgb, ansi_encoder/white, iso_encoder/rgb, iso_encoder/white | vial-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| K1 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K2 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K3 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K5 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white | vial-keychron |
| K7 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| K8 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-updated-keychron |
| K9 MAX | ansi/rgb, ansi/white | vial-updated-keychron |
| K10 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white | vial-keychron |
| K11 MAX | ansi_encoder/rgb, ansi_encoder/white, iso_encoder/rgb, iso_encoder/white | vial-keychron |
| K13 MAX | ansi/rgb, ansi/white, iso/rgb, iso/white | vial-keychron |
| K15 MAX | ansi_encoder/rgb, ansi_encoder/white, iso_encoder/rgb, iso_encoder/white | vial-keychron |
| K17 MAX | ansi_encoder/rgb, ansi_encoder/white, iso_encoder/rgb, iso_encoder/white, jis_encoder/rgb, jis_encoder/white | vial-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| K2 HE | ansi, iso, jis | vial-updated-keychron |
| K4 HE | ansi, iso, jis | vial-updated-keychron |
| K6 HE | ansi | vial-updated-keychron |
| K8 HE | ansi, iso, jis | vial-updated-keychron |
| K10 HE | ansi, iso | vial-updated-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| K3 VERSION 3 | ansi/rgb, ansi/white, iso/rgb, iso/white, jis/rgb, jis/white | vial-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| C1 PRO | ansi/rgb, ansi/white | vial-updated-keychron |
| C2 PRO | ansi/rgb, ansi/white | vial-updated-keychron |
| C3 PRO | ansi/red, ansi/rgb | vial-updated-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| C1 PRO V2 | ansi/non_light, ansi/rgb, ansi/white | vial-updated-keychron |
| C2 PRO V2 | ansi/rgb, ansi/white | vial-updated-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| C1 PRO 8K | ansi, iso, jis | vial-updated-keychron |
| C2 PRO 8K | ansi, iso | vial-updated-keychron |
| C3 PRO 8K | ansi, iso, jis | vial-updated-keychron |
| Keyboard | Layouts | Branch |
|---|---|---|
| S1 | ansi/rgb, ansi/white | vial-updated-keychron |
| X0 | red | vial-updated-keychron |
- Real-time keymap editing
- Layer management
- Macro programming
- Tap dance configuration
- Combo keys
- Key overrides
- QMK Settings
Configure your wireless Keychron keyboard through the 2.4 GHz USB dongle (Keychron Link) — no USB cable needed:
- Transparent VIA/Vial tunneling: All standard and Keychron-specific features work wirelessly, exactly as they do over USB
- Supported dongles: Keychron Link USB-A (
3434:D030) and USB-C (3434:D031) - Automatic detection: The GUI detects the bridge dongle and wirelessly-connected keyboard automatically
- USB+dongle deduplication: When the same keyboard is connected via both USB and dongle, the USB connection is preferred
- Non-blocking connection: The GUI stays responsive while establishing the wireless link
- Requires firmware support: The keyboard must be running the Vial firmware from the
vial-updated-keychronbranch with the wireless XOR encoding patch (required to work around a hardware limitation in the LKBT51 wireless module)
- Debounce: Adjust key debounce time (1-20ms)
- NKRO: Toggle N-Key Rollover
- Report Rate: 125Hz / 250Hz / 500Hz / 1000Hz (8000Hz on supported models)
- Wireless Low Power Mode: Battery optimization for wireless keyboards
Configure Simultaneous Opposite Cardinal Direction handling for non-HE keyboards:
- Define key pairs (e.g., A+D, W+S)
- Choose resolution mode: Last Input, First Key, Second Key, or Neutral
- Global RGB Mode: Select from 40+ RGB effects
- Per-Key RGB: Set individual key colors
- Mixed RGB: Combine effects with per-key overrides
- OS Indicators: Customize Caps Lock, Num Lock, Scroll Lock LED colors
- Multiple named profiles, each storing a full independent configuration
- Switch between profiles on the fly; save or reset per profile
- Multiple Modes per key:
- Global — inherit the profile's default settings
- Regular — fixed actuation point
- Rapid Trigger — dynamic actuation; re-triggers on direction change
- Dynamic Keystroke (DKS) — two travel zones (shallow/deep), each triggering up to 4 keycodes with configurable actions (Press, Release, Tap, Re-press)
- Gamepad — assign key to a joystick axis or button (see below)
- Toggle — key toggles its held state on each press
- Actuation Point: Per-key or global, 0.1mm–4.0mm
- Rapid Trigger: Separate press and release sensitivity (0.1mm–4.0mm); bottom dead zone prevents false re-triggers near bottom-out
Configure Simultaneous Opposite Cardinal Direction resolution per key pair:
- Deeper Travel Wins — whichever key is pressed further takes priority
- Deeper Travel Wins (Single) — as above, but holds last state if both fully bottomed out
- Last Input Wins — most recently pressed key takes priority
- Key 1 Priority / Key 2 Priority — one key always wins
- Neutral — both keys cancel each other out
- Up to 20 configurable pairs per profile
Keys in Gamepad mode can be assigned to standard HID joystick or XInput (Xbox controller) inputs:
- HID Joystick: 6 axes (X, Y, Z, RX, RY, RZ); up to 32 buttons
- XInput: Full Xbox 360 controller emulation — left/right sticks, analog triggers (LT/RT), 16 buttons (A, B, X, Y, LB, RB, Back, Start, L3, R3, D-pad)
- Typing + Gaming: Optional mode where keyboard input stays active alongside gamepad
- Circular normalization: Diagonal stick directions are scaled to stay within the circular input boundary
- Response Curve: 4-point piecewise linear curve maps key travel to analog axis value
- Auto-calibration: Runs at power-on and monitors for sensor drift
- Manual calibration: Two-phase (zero travel → full travel → save)
- Real-time key travel monitor: Live travel depth readout per key for diagnostics
- Read calibrated values: Inspect per-key zero/full travel and scale factor
Built-in firmware flasher in the GUI — no external tools needed on desktop:
- Automatic layout backup: Saves current keymap, macros and Keychron settings before flashing
- One-click flash: Reboots keyboard into DFU mode, waits for DFU device, flashes
.binwithdfu-util, then restores layout automatically - Progress bar: Live erase and download progress from
dfu-util - MCU/firmware info: Displays detected MCU type and current firmware version before flashing
- Web support: Also available in the browser via WebUSB DfuSe
- Visit vial.tymon3310.dev
- Click "Start Vial"
- Select your keyboard from the WebHID prompt
- Configure your keyboard
Note: Keychron-specific tabs may not be fully functional in the web version.
If you are using Arch Linux, you can install the GUI via the AUR.
Recommended (Pipette):
pipette-desktop-keychron-bin(Pre-compiled)pipette-desktop-keychron-git(Build from source)
Legacy (vial-gui):
vial-keychron-bin(Pre-compiled)vial-keychron-git(Build from source)
# Example using yay
# Recommended (Pipette)
yay -S pipette-desktop-keychron-bin
# Legacy (vial-gui)
yay -S vial-keychron-binPipette is the modern, Electron-based alternative to the original Vial GUI. It features a cleaner UI and faster performance.
- Download the latest release from the Pipette GitHub Releases.
- For Linux, download the
.AppImage, make it executable, and run:chmod +x Pipette-linux-x86_64.AppImage ./Pipette-linux-x86_64.AppImage
- For Windows/macOS, use the provided installers.
-
Clone the repository:
git clone --branch vial-keychron https://github.com/tymon3310/pipette-desktop.git cd pipette-desktop -
Install dependencies (requires pnpm):
pnpm install
-
Run the app:
pnpm dev
OR build the production app:
pnpm build pnpm dist:linux # or dist:win, dist:mac
Use this if you prefer the original Python-based interface or encounter issues with Pipette.
- Download the latest release from the vial-gui GitHub Releases.
- For Linux, download the
.AppImage, make it executable, and run:chmod +x Vial-*.AppImage ./Vial-*.AppImage
- For Windows/macOS, use the provided installers.
-
Clone the vial-gui repository:
git clone --branch vial-keychron https://github.com/tymon3310/vial-gui.git cd vial-gui -
Install dependencies:
uv venv .venv --python 3.12 source .venv/bin/activate # Linux/macOS # or: .venv\Scripts\activate # Windows uv pip install -r requirements.txt
-
Run the app:
python src/main/python/main.py
OR Compile the app:
pyinstaller misc/Vial.spec --noconfirm ./dist/Vial/Vial
-
Clone the vial-qmk repository:
# Active branch (recommended) git clone --branch vial-updated-keychron https://github.com/tymon3310/vial-qmk.git # Legacy branch (for keyboards not yet ported) git clone --branch vial-keychron https://github.com/tymon3310/vial-qmk.git cd vial-qmk
-
Set up QMK:
qmk git submodule
-
Build and flash firmware for your keyboard:
# Compile only qmk compile -kb keychron/v5_max/ansi_encoder -km vial # Or compile and flash in one step (puts keyboard in bootloader mode automatically) qmk flash -kb keychron/v5_max/ansi_encoder -km vial
qmk flashwill prompt you to put the keyboard in bootloader mode (hold Esc while plugging in, or press the reset button). For subsequent updates once Vial is running, the GUI flasher is more convenient as it can restore settings — see Flashing Instructions.
Note: The GUI Flasher tab requires the keyboard to already be running Vial firmware. For a first-time flash from stock firmware, use the manual method.
The desktop and web apps have a built-in DFU Firmware updater tab — use this to update firmware on a keyboard that is already running Vial:
- Open the app and connect your keyboard
- Go to the DFU Firmware updater tab
- Select your compiled
.binfile - Click Flash — the GUI will back up your layout, reboot the keyboard into DFU mode, flash the firmware, and if you selected to restore current layout (on by default), restore your layout automatically
The GUI flasher on desktop calls
dfu-utilunder the hood and works on Windows, Linux, and macOS — as long asdfu-utilis installed and on PATH.
- Linux: use your's system package manager (ex,
sudo pacman -S dfu-util)- Windows:
scoop install dfu-util(via scoop), or download from dfu-util.sourceforge.net and add to PATH- macOS:
brew install dfu-utilThe web app flasher does not require
dfu-util, but on Windows you need to install additional drivers. You can download the driver installer from QMK Toolbox repo.
Use this for a first-time flash from stock firmware, or any time the GUI flasher is not available:
-
Enter bootloader mode:
- Hold Esc while plugging in the USB cable, OR
- Press the reset button under the spacebar
-
Flash with dfu-util:
# STM32 (most Keychron keyboards) dfu-util -a 0 -d 0483:DF11 -s 0x8000000:leave -D <firmware.bin>
-
The keyboard reboots automatically after flashing (
:leaveflag).
- Ensure you're using the Vial firmware (not stock QMK or VIA)
- Check that the keyboard is in wired mode (not Bluetooth)
- On Linux, add udev rules:
export USER_GID=`id -g`; sudo --preserve-env=USER_GID sh -c 'echo "KERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", ATTRS{serial}==\"*vial:f64c2b3c*\", MODE=\"0660\", GROUP=\"$USER_GID\", TAG+=\"uaccess\", TAG+=\"udev-acl\"" > /etc/udev/rules.d/59-vial.rules && udevadm control --reload && udevadm trigger'
The Keychron-specific tabs only appear when:
- Using a compatible GUI (Pipette or custom vial-gui fork)
- Keyboard firmware has Keychron features enabled
- Keyboard supports the specific feature (e.g., Analog Matrix only on HE keyboards)
- Ensure VialRGB is enabled in firmware
- Check that the keyboard declares
"lighting": "vialrgb"in vial.json - Use a compatible GUI (Pipette or custom vial-gui fork) - stock Vial GUI may not support all effects
Contributions are welcome! Please:
- Fork the relevant repository
- Create a feature branch
- Submit a pull request
- Create the keyboard folder in
keyboards/keychron/<keyboard_name>/ - Add
keymaps/vial/with:keymap.cconfig.h(with uniqueVIAL_KEYBOARD_UID)vial.json(keyboard layout definition)rules.mk
- Generate a unique UID:
python3 util/vial_generate_keyboard_uid.py - Test the build and flash
- vial-qmk: GPL-2.0 (inherited from QMK)
- vial-gui: GPL-2.0 (inherited from Vial)
- This documentation: MIT
- QMK Firmware - The foundation
- Vial - Real-time keyboard configuration
- Keychron - Original keyboard firmware
- Pipette - Modern Electron-based GUI
- Community contributors
- Claude Opus for fixing some annoying stuff that for some reason just stopped working on it's own