DOCS
Using keybars
keybars is the free command-line version: it listens through cava and drives your keyboard through Keymapp or OpenRGB. keybars Pro is the desktop app, which needs neither.
Requirements
- A keyboard with a backend:
- cava to listen to the audio (
dnf install cava,apt install cava,brew install cava). macOS also needs a loopback device such as BlackHole: see macOS audio. - Go 1.25 or later, only to build from source
Install
brew install jwarykowski/tap/keybars # macOS or Linux; brings cava too
go install github.com/jwarykowski/keybars@latest # or from source
Releases also have binaries for macOS and Linux on GitHub.
Usage
keybars # the connected keyboard, bars up to the number row
keybars -model voyager # pick a built-in map
keybars -map my-board.txt # or your own
keybars gen zsa/voyager # write a map from QMK's definition
keybars -height 5 # use the number row too
keybars -colours '#00ff00,#ffff00,#ff0000' # meter colours, bottom to top
keybars -gain 2 # reach the top more often (default 1.5)
keybars -peaks=false # no peak dots
keybars -reserve 36,61 # never touch these LEDs
keybars -smooth 15 # jumpier (0-100, default 35)
keybars -backend openrgb # the first keyboard OpenRGB has
keybars -backend openrgb -device apex # or the one whose name contains apex
keybars -source 'BlackHole 2ch' # cava capture device (macOS needs a loopback)
keybars version # the installed version
Stop it with Ctrl-C. -v prints the frame rate on exit.
When the music stops, the keys go back to your layout’s colours, and the same when keybars exits. On ZSA boards, keys outside the bars are dark while it plays: once Keymapp drives any key, the firmware shows only what it sets.
Config file
keybars and keybars start read their settings from ~/.config/keybars/config ($XDG_CONFIG_HOME/keybars/config when that is set), one flag per line by its own name. Flags on the command line override it, and -config FILE reads another file.
# ~/.config/keybars/config
source = BlackHole 2ch
colours = #00ff00,#ffff00,#ff0000
gain = 2
peaks = false
Lines starting with # are comments; a # later in a line is part of the value, as in the colours. An unknown setting stops keybars with its line number, except keybars Pro’s own (low-hz, high-hz, brightness, symmetry), which keybars 0.4 and later skip so one file works in both. The config builder writes one with a live preview.
Running in the background
keybars start # run in the background with the config file's settings
keybars start -gain 3 # flags work here too
keybars status # running or not, and where the log is
keybars stop # hand the keys back to the layout and exit
start keeps a pid file and log in ~/.local/state/keybars/ ($XDG_STATE_HOME/keybars/). If keybars fails straight away, say Keymapp isn’t running, start prints the error instead.
Starting at login
On macOS, save this as ~/Library/LaunchAgents/dev.jwarykowski.keybars.plist, with the path to your keybars:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>dev.jwarykowski.keybars</string>
<key>ProgramArguments</key>
<array><string>/Users/you/go/bin/keybars</string></array>
<key>EnvironmentVariables</key>
<dict><key>PATH</key><string>/opt/homebrew/bin:/usr/bin:/bin</string></dict>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>ThrottleInterval</key><integer>10</integer>
<key>StandardErrorPath</key><string>/tmp/keybars.log</string>
</dict>
</plist>
Load it with launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.jwarykowski.keybars.plist. keybars exits when Keymapp or the keyboard goes away, and KeepAlive starts it again every 10 seconds until they’re back, so set Keymapp to open at login too. PATH is there so launchd finds cava.
On Linux, a systemd user unit does the same, in ~/.config/systemd/user/keybars.service:
[Unit]
Description=keybars
[Service]
ExecStart=%h/go/bin/keybars
Restart=always
RestartSec=10
[Install]
WantedBy=default.target
Then systemctl --user enable --now keybars.
Other audio sources
Any program can drive the bars instead of cava: -stdin reads cava’s raw ascii format, one frame per line, one height (0 to the bar height) per column:
printf '1;2;3;4;3;2;1;0;1;2;3;4;\n' | keybars -stdin
OpenRGB
With -backend openrgb, keybars connects to OpenRGB’s SDK server (-openrgb, default 127.0.0.1:6742), takes the keyboard into its per-LED mode while music plays, and puts its previous mode back afterwards. Keys outside the bars keep their colours if the keyboard was already per-LED, and go dark otherwise.
Most keyboards in OpenRGB come with a key matrix, which keybars uses as the map: the top row (usually Esc and the F-keys) is left free by default, like the number row on ZSA boards. keybars maps -backend openrgb lists the devices and prints that map, to save and edit for -map. For a keyboard without a matrix, keybars find -backend openrgb lights LEDs from index 0 with their OpenRGB names.
When OpenRGB rescans its devices, or the keyboard is unplugged and plugged back in, keybars finds it again and carries on.
macOS audio
macOS gives programs no way to listen to what it plays, so cava listens to a loopback device instead:
- Install BlackHole (
brew install blackhole-2ch). - In Audio MIDI Setup, add a Multi-Output Device with BlackHole 2ch and your speakers or headphones. Make the speakers or headphones the primary device and tick Drift Correction for BlackHole.
- Choose the Multi-Output Device as the sound output (Control Centre > Sound). Connecting Bluetooth headphones switches the output back to them, so pick it again after. The volume keys don’t work on a Multi-Output Device; use the headphones’ or speakers’ own.
- Run
keybars -source 'BlackHole 2ch', or putsource = BlackHole 2chin the config file.
keybars Pro skips all of this: it records the system’s audio itself on macOS 14.2 and later.
If the bars stay dark
keybars testlights the keys without audio, so it shows whether Keymapp is the problem.- macOS counts listening to BlackHole as using the microphone. Allow your terminal (or keybars, under launchd) in System Settings > Privacy & Security > Microphone; without it cava hears silence and says nothing.
- In QuickTime, File > New Audio Recording with BlackHole 2ch as the input shows whether sound reaches BlackHole at all.
- A cava config with
method = portaudioandsource = listunder[input]lists the devices cava can see, by the names-sourcetakes.
Keyboard maps
A map is the backend’s index for each key (Keymapp’s, or OpenRGB’s), drawn as the keyboard looks: one line per row of keys, top to bottom. Columns become bars and rows become levels, so any size works. - marks a gap, | is decoration and # starts a comment. The Moonlander’s:
- 1 2 3 4 5 | 37 38 39 40 41 42
7 8 9 10 11 12 | 44 45 46 47 48 49
14 15 16 17 18 19 | 51 52 53 54 55 56
21 22 23 24 25 26 | 57 58 59 60 61 62
27 28 29 30 31 - | - 63 64 65 66 67
Keymapp numbers each half on its own, row by row from the left, inner column included. keybars maps lists the built-in maps and which one the connected keyboard uses, matched on the name Keymapp reports.
Adding a keyboard
If QMK knows the keyboard, keybars gen writes its map from QMK’s definition in qmk_firmware/keyboards:
keybars gen zsa/voyager > voyager.txt # fetched from qmk_firmware
keybars gen path/to/keyboard.json # or a local copy
keybars gen -order firmware zsa/moonlander # QMK's own LED order
gen follows Keymapp’s numbering by default; -order firmware gives QMK’s rgb_matrix order, for backends that talk to the firmware directly. The thumb cluster and a split half’s short inner column are left out (-inner keeps the latter). Definitions that inherit from a parent info.json aren’t followed yet, so pass a merged file (qmk info -kb <keyboard> -f json) for those.
Otherwise, find the indices by hand:
keybars finddarkens the board and lights one key at a time, printing its index (Keymapp never lights index 0, so start from 1). Press Enter for the next, and write each index where its key sits.keybars test -map my-board.txtsweeps a bar across each column, then a level across each row: a key out of line is a wrong index.- Use it with
-map my-board.txt, or open a pull request adding it tomaps/as<model>.txt, named so it appears in Keymapp’s name for the keyboard (ergodox.txtfor “ErgoDox EZ”).
Support
keybars is free and MIT licensed. Bugs and new keyboard maps are welcome on GitHub, and if it makes your desk more fun, you can sponsor its development.