Framework Laptop 16 LED Matrix “VU / equalizer” driven from PipeWire audio via CAVA.
This project runs a small user-level service that:
- Reads audio levels from PipeWire (through
pipewire-pulse) usingcavaraw ASCII output. - Drives the left/right LED Matrix modules either in stereo (left channel -> left module, right channel -> right module) or mono.
- Supports per-module bar mirroring (
mirror_left/mirror_right). - Resolves your module symlink paths to real
/dev/ttyACM*paths viareadlink -fbefore opening serial connections, then uses a persistent serial connection for low-latency updates.
sudo pacman -S --needed cava pythonYou also need ledmatrixctl working (from framework16-inputmodule).
All commands below assume you are running from the repo root.
This will create ./config.ini if missing, install the systemd user unit, and start it:
bash ./install.shTo override which Python is used (recommended: your framework16-inputmodule venv python):
FW16_LED_VU_PYTHON=/home/jack/fw16-ledmatrix/.venv/bin/python bash ./install.shConfig is stored at the repo root as ./config.ini.
Edit it:
$EDITOR ./config.iniConfig changes are auto-applied by the running service (typically within ~1 second). No manual restart needed.
Make sure [devices] left and [devices] right point to your symlinks, e.g.:
[devices]
left = /dev/fw16-ledmatrix-left
right = /dev/fw16-ledmatrix-rightThe runner will resolve each via readlink -f into a real /dev/ttyACM* path.
In ./config.ini:
[audio]
mode = stereomode = mono: CAVA usesmono_option = average, and both modules show the same EQ.mode = stereo: the runner starts two CAVA instances internally:- left module uses
mono_option = left - right module uses
mono_option = right
- left module uses
Confirm CAVA produces frames:
cava -p ./cava.confNote: cava.conf is a base config. When mode = stereo, the runner generates temporary CAVA configs internally and overrides mono_option to left and right.
Then run the runner:
/home/jack/fw16-ledmatrix/.venv/bin/python ./fw16_ledvu.py --config ./config.iniIn ./config.ini:
[eq] max_value: must be <= 34 (the built-inledmatrixctl --eqtreats values as a 0..34 “height”).[eq] update_hz: how often to send to the devices (throttling).[eq] gain+[eq] gamma: volume curve. For example:gain = 1.5andgamma = 0.7will boost quieter signals while keeping loud signals closer to the same.[eq] compress_threshold+[eq] compress_ratio: tame louder signals (simple compression). Example:compress_threshold = 0.7,compress_ratio = 3.0.[eq] rise_smoothing+[eq] fall_smoothing: separate smoothing for rising vs falling values ((0) disables smoothing).[eq] floor(aliasnoise_gate): ignore tiny values so the display truly rests at 0.[eq] peak_hold_ms: hold peaks briefly for readability ((0) disables).[eq] silence_floor+[eq] silent_update_hz: reduces updates during silence.[devices] enable_battery_saver: if true, the program turns the display off while on battery power and turns it back on when AC returns.
install.sh: installs./config.ini(if missing), generates~/.config/systemd/user/fw16-ledvu.service, and starts the user service.fw16_ledvu.py: runner (CAVA ->ledmatrixctl --eq).cava.conf: CAVA config for 9 bars, raw ASCII output.config.example.ini: template you copy to./config.ini.