There wasn't a nice native soundboard app for GNOME, so I vibecoded one over a couple of days with Claude. It turned into a GTK 4 + Libadwaita app written in Rust with polyphonic playback, a sequential queue, a real-time LCD panel with oscilloscope and scrubbing, per-sound editing (start/trim/fades), global numpad hotkeys, a PipeWire virtual microphone, and a real-time mic effects chain (built-in + LV2 plugins, with presets).
⚠️ Use at your own risk. This is a hobby project that is largely AI-generated and lightly tested. It pokes at your PipeWire graph and sets your default input device, so expect rough edges, don't run it in the middle of an important call. No warranty, etc. etc.
- Compact tile grid — load any number of audio files; play/cue on the tile, volume + editor + rename/remove in the tile's ⋮ menu; search box to filter; drag a tile onto another to reorder (order is saved)
- Per-sound settings, persisted — volume, start marker, end trim and fade in/out are saved per sound (
config.json) and survive renames; volume changes apply live to already-playing sounds - Sound editor — per-sound dialog with the decoded waveform: drag the orange start marker / red end-trim marker, set fades, and preview from the marker (monitor-only, with playhead)
- Selection + one-shot start — click a tile to select it, then drag the LCD progress bar to set a one-shot start point that only the next play uses; the transport preview (headphones) and play buttons act on the selection
- Scrub while playing — drag the progress bar during playback to seek
- Loudness normalization — newly imported sounds are analyzed and their volume pre-set so peaks land near −1 dBFS (loud files are only ever turned down)
- Global hotkeys — Ctrl+Alt + a tile number typed on the numpad plays that tile (e.g.
56→ tile 56); Ctrl+Alt+Numpad Enter stops everything (XDG GlobalShortcuts portal; GNOME asks for approval once) - Polyphonic & sequential modes — play (and stack) sounds simultaneously, or queue them up; toggled in Settings
- Cue button — always appends to the queue, regardless of mode
- Skip — stops all currently playing sounds and immediately starts the next item in the queue
- LCD playback panel — scrollable track list with per-track countdown, a live oscilloscope of the playing mix, aggregate time totals, and the interactive progress bar
- Rename — rename sound tiles with an in-place dialog; the file on disk, saved settings and all playback references follow
- Remove with undo — moves the file out of the Sounds Folder (cross-filesystem safe), removes the tile, and shows a 6-second undo toast; permanent deletion happens on dismiss
- Sounds Folder — configure a folder in Settings; all audio files found there are loaded automatically on launch
- Sounds Folder + Move Added Files — where sounds live, and whether new files are moved there
- Input Device — pick which physical microphone feeds the effects chain (default = auto-detect)
- Monitor Output — hear the soundboard yourself on the system default output (toggle); monitor level is the slider on the soundboard bar
- Playback — Play Multiple Sounds (polyphonic) and Default Volume for new tiles
- Virtual Device — set the virtual mic's display name and whether it is created on launch
- Startup — Start on Login (run hidden in the background and keep mic effects active)
- PipeWire virtual mic — Resonate exposes a "Resonate Microphone" source that other apps (Discord, OBS, browsers…) can select as an input
- Sounds + mic mixed together — soundboard playback and your real microphone are summed into the virtual mic, so others hear both
- Set as default input on launch — the virtual mic is made the system default input automatically
- Survives across logins — a PipeWire drop-in (
~/.config/pipewire/pipewire.conf.d/resonate.conf) keeps a raw mic pass-through alive even when Resonate isn't running
- Real-time effects chain applied to the microphone, grouped into Voice & Cleanup and Character & Fun:
- Voice & Cleanup — built-in Noise Gate and Gain, plus curated LV2 (hosted via
livi): Noise Suppression (RNNoise), Auto Gain, Compressor, De-esser, Limiter and Parametric EQ — the same plugins Easy Effects wraps - Character & Fun — built-in Distortion, Bitcrusher and Telephone, plus Pitch Shifter (chipmunk/demon), Auto-Tune, Ring Modulator, Vocoder, Reverb, Vintage/Reverse Delay, Rotary Speaker, Multi Chorus, Flanger, Phaser, Pulsator, Saturator, Crusher and Tape Simulator
- Voice & Cleanup — built-in Noise Gate and Gain, plus curated LV2 (hosted via
- Add / remove / reorder effects from the Effects page (the same effect may be added twice — two delays, an EQ each side of a compressor); controls are generated automatically per plugin and rendered by type — sliders, switches (toggles) and dropdowns (enumerated choices). Plugins with a lot of controls show the important ones first and fold the rest into an All parameters expander
- Chain presets — save the current chain under a name and switch between chains in one click, from the app or the tray menu (which marks the active one, or "Custom" once you tweak a knob). Resonate ships a set to start from — Podcast, Broadcast, Noisy Room, Old Radio, Robot, Vocoder Robot, Demon, Chipmunk, Stadium Announcer, Cathedral — and hides the ones whose plugins aren't installed
- Per-effect presets — a Preset dropdown above each effect's knobs with ready-made starting points ("Podcast", "Radio DJ", "Chipmunk", "Stadium announcer"), so a 70-knob compressor is one pick instead of an afternoon. Presets shipped by the plugin itself (Calf's, say) show up in the same list
- Level meter — live post-effects mic level, so you can set the gate threshold by eye
- Chain and presets are persisted to
config.jsonand re-applied on launch. (The Add sheet only lists the curated plugins, but a chain referencing any other installed LV2 id still loads.)
- Run in the background — closing the window hides Resonate instead of quitting, so the mic effects keep processing
- Tray indicator — a StatusNotifierItem with Show Resonate / Quit (on GNOME this needs the AppIndicator and KStatusNotifierItem Support shell extension)
- Start on Login — a Settings switch installs an autostart entry that launches Resonate hidden, so your processed mic is available right after login
| Dependency | Version / Notes |
|---|---|
| Rust | stable (edition 2024) |
| GTK 4 | ≥ 4.14 |
| Libadwaita | ≥ 1.6 |
| PipeWire | system default on Fedora |
| LV2 / lilv (dev) | needed to build the LV2 host |
Audio decoding is handled entirely in Rust via rodio + Symphonia — no GStreamer headers required.
Supported audio formats: WAV, MP3, FLAC, OGG, AAC, M4A, Opus, WMA.
sudo dnf install lilv-devel lv2-devel serd-devel sord-devel sratom-devel dbus-devel# Debug build
cargo build
cargo run
# With verbose logging
RUST_LOG=resonate=debug cargo run
# Launch hidden in the background (used by the autostart entry)
cargo run -- --hiddenA spec for a local, installable package lives in packaging/resonate.spec:
sudo dnf install rpm-build rpmdevtools
rpmdev-setuptree
VERSION=$(awk -F'"' '/^version/{print $2; exit}' Cargo.toml)
git archive --format=tar.gz --prefix=resonate-$VERSION/ \
-o ~/rpmbuild/SOURCES/resonate-$VERSION.tar.gz HEAD
rpmbuild -bb packaging/resonate.spec
sudo dnf install ~/rpmbuild/RPMS/*/resonate-$VERSION-*.rpmThe package installs the binary to /usr/bin/resonate and the desktop entry +
icon under /usr/share, and softly recommends lsp-plugins-lv2 for a ready-made
set of LV2 effects.
Resonate hosts any LV2 plugin found on the standard search path (~/.lv2,
/usr/lib64/lv2, …). Useful packages on Fedora:
# Voice: a large, high-quality suite (gate, compressor, autogain, EQ, …)
sudo dnf install lsp-plugins-lv2
# Character & fun: reverb, delays, ring modulator, vocoder, rotary speaker,
# chorus/flanger/phaser, saturator, crusher, tape
sudo dnf install lv2-calf-plugins
# Pitch shifting (chipmunk/demon) and auto-tune
sudo dnf install lv2-rubberband-plugins lv2-x42-plugins
# Noise cancellation (via the audinux COPR)
sudo dnf copr enable ycollet/audinux
sudo dnf install noise-repellentThe Add Effect sheet shows the built-ins plus a curated set of installed LV2 plugins, grouped by category (discovery runs once when the Effects page first opens); curated effects whose plugin isn't installed are simply not listed. A handy mic chain, in order: Noise Gate → Noise Suppression (RNNoise) → Auto Gain → Gain — or just load the Podcast chain preset. For a silly one, try the Robot, Demon or Vocoder Robot chains.
GNOME matches a running window to its .desktop file by app ID
(io.github.kilo2071.Resonate), so the desktop entry's basename, its Icon=,
and the installed icon file must all use that exact ID — otherwise the Overview
and Alt+Tab show a generic name/icon.
For a development build, install the committed desktop entry and icon once
(pointing Exec at your built binary):
mkdir -p ~/.local/share/icons/hicolor/scalable/apps ~/.local/share/applications
cp data/icons/io.github.kilo2071.Resonate.svg ~/.local/share/icons/hicolor/scalable/apps/
sed "s|^Exec=resonate|Exec=$PWD/target/debug/resonate|" \
data/io.github.kilo2071.Resonate.desktop \
> ~/.local/share/applications/io.github.kilo2071.Resonate.desktop
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor/
update-desktop-database ~/.local/share/applications/(An RPM install puts the same files under /usr/share with Exec=resonate.)
src/
main.rs — GTK/Adwaita init, GResources, CSS, app entry
application.rs — AdwApplication subclass (registers --hidden)
window.rs — AdwApplicationWindow; audio/effects/hotkey wiring, rename/remove logic
config.rs — Config, EffectEntry, per-sound SoundSettings (serde_json)
hotkeys.rs — global hotkeys via the GlobalShortcuts portal (blocking dbus)
tray.rs — StatusNotifierItem tray (ksni), incl. preset submenu
audio/
engine.rs — rodio polyphonic engine, queue, tick loop, seek, envelope, scope feed
virtual_device.rs — in-process PipeWire streams: bridge, soundboard + mic capture/playback
pw_config.rs — routing plan, mic detection, loopback teardown, drop-in persistence
wave.rs — waveform peak data shared by the editor and the LCD
sampler.rs — (legacy stub)
plugins/
mod.rs — ResonatePlugin trait, PluginParam, BUILTINS + CURATED_LV2 registries
host.rs — PluginChain
lv2.rs — LV2 host (livi): discovery, Lv2Plugin, plugin-shipped presets
chains.rs — chain presets that ship with the app
layout.rs — which parameters each effect shows first
presets.rs — per-effect presets (curated + plugin-shipped)
builtin/
gain.rs — Gain (0.0–4.0× linear)
gate.rs — Noise Gate (RMS, attack/release)
distortion.rs — tanh waveshaper (drive/mix/level)
bitcrush.rs — bit depth + sample-and-hold downsampling
telephone.rs — band-limit filter (old phone/radio)
ui/
soundboard_page.rs — sound grid, selection, LCD panel (oscilloscope, progress scrub)
sound_tile.rs — individual tile widget (menu: volume, edit, rename, remove)
sound_editor.rs — waveform editor dialog (markers, fades, preview)
effects_page.rs — mic effects chain editor (built-in + curated LV2, presets, meter)
settings_page.rs — folder picker, virtual device / input settings
resources/
ui/ — GTK XML UI templates
style.css — LCD panel and overlay styles
data/
icons/ — app icon SVG
See CLAUDE.md for the detailed audio architecture (the virtual-mic bridge, the
in-process mic FX path, and PipeWire gotchas).
io.github.kilo2071.Resonate
Config is stored at ~/.config/io.github.kilo2071.Resonate/config.json.