Skip to content

appliedapp/rusty_lights

Repository files navigation

RustyLights

CI License: GPL v3

Resource-efficient audio-reactive LED visualizer written in Rust. Captures audio, processes it through a DSP pipeline, and outputs to LED strips via E1.31/sACN, DDP, or Art-Net.

Designed to run on a Raspberry Pi with sub-5% CPU usage.

Features

  • Audio backends: PipeWire, ALSA, FIFO (for MPD-based players)
  • DSP pipeline: FFT, Mel filterbank, attack/release smoothing, AGC, beat detection
  • 7 effects: energy, spectrum, scroll, reactive, pulse, vumeter, chromafreq
  • 7 gradients: rainbow, fire, ocean, forest, sunset, party, lava
  • Output protocols: E1.31/sACN, DDP (WLED), Art-Net
  • Multi-universe: auto-splits large LED arrays across universes

Quick Start

Build

# Desktop (PipeWire, default)
cargo build --release

# For systems with ALSA only (e.g. moOde, Raspberry Pi)
cargo build --release --no-default-features --features alsa

# FIFO-only (no audio library dependencies)
cargo build --release --no-default-features

Run

# Start visualizer (auto-detects /etc/rusty_lights.conf or ./rusty_lights.toml)
rusty_lights run

# Use a specific config file
rusty_lights -c /path/to/config.toml run

# List audio devices
rusty_lights devices

# List available effects
rusty_lights effects

# Send test pattern to LEDs
rusty_lights test

# Verbose logging
rusty_lights run -vv

Configuration

Without -c, the config file is resolved in order:

  1. /etc/rusty_lights.conf
  2. ./rusty_lights.toml

If neither exists, built-in defaults are used.

# System-wide (recommended for Pi deployments)
sudo cp rusty_lights.example.toml /etc/rusty_lights.conf

# Or local
cp rusty_lights.example.toml rusty_lights.toml
[audio]
backend = "fifo"                # pipewire | alsa | fifo
fifo_path = "/tmp/mpd.fifo"    # for FIFO backend
sample_rate = 48000

[dsp]
smoothing = 0.6                 # 0.0-1.0 (release time, higher = slower fade)
beat_sensitivity = 1.5          # 1.0-3.0 (higher = fewer beats detected)

[effect]
name = "chromafreq"             # energy | spectrum | scroll | reactive | pulse | vumeter | chromafreq

[effect.params]
brightness = 1.0

[output]
protocol = "ddp"                # e131 | ddp | artnet
target = "192.168.1.100"        # IP of LED controller, "auto" for WLED mDNS discovery
fps = 60
idle_timeout = 0                # Minutes without audio before closing connection (0 = disabled)

[output.leds]
count = 240                     # Optional with WLED — auto-detected from /json/info if omitted
rgb_order = "GRB"               # Optional with WLED — auto-detected from /json/cfg if omitted

[http]
enabled = false                 # Web UI with live visualization and effect controls
port = 8080

Cross-Compilation for Raspberry Pi

Cross-compilation uses cross which handles all toolchains and libraries via containers. Works with Docker or Podman.

Setup (once)

cargo install cross

# If using Podman instead of Docker:
export CROSS_CONTAINER_ENGINE=podman

Raspberry Pi 4 / Pi 3B+ (64-bit, aarch64)

cross build --release --target aarch64-unknown-linux-gnu --no-default-features --features alsa
scp target/aarch64-unknown-linux-gnu/release/rusty_lights pi@<pi-ip>:~/

Raspberry Pi 3 / Pi 2 / Pi Zero 2 W (32-bit, armv7)

cross build --release --target armv7-unknown-linux-gnueabihf --no-default-features --features alsa
scp target/armv7-unknown-linux-gnueabihf/release/rusty_lights pi@<pi-ip>:~/

Raspberry Pi Zero / Zero W (armv6)

cross build --release --target arm-unknown-linux-gnueabihf --no-default-features --features alsa
scp target/arm-unknown-linux-gnueabihf/release/rusty_lights pi@<pi-ip>:~/

Size-optimized build (for constrained devices)

Add --profile release-small to any of the above commands for a smaller binary (uses opt-level = "s" and fat LTO).

Installation

Quick install (Debian/Raspberry Pi OS)

curl -sSL https://raw.githubusercontent.com/appliedapp/rusty_lights/main/install.sh | sudo sh

This downloads the latest .deb package for your architecture, installs the binary, config, and systemd service. Then:

sudo nano /etc/rusty_lights.conf          # adjust to your setup
sudo systemctl enable --now rusty_lights

Pre-built .deb packages for amd64, arm64, and armhf are available on the Releases page.

Manual install

Install the binary and config on your Pi, then create a service unit:

sudo cp rusty_lights /usr/local/bin/
sudo cp rusty_lights.example.toml /etc/rusty_lights.conf
# Edit /etc/rusty_lights.conf to match your setup

Create /etc/systemd/system/rusty_lights.service:

[Unit]
Description=RustyLights audio-reactive LED visualizer
After=network.target sound.target

[Service]
ExecStart=/usr/local/bin/rusty_lights run
Restart=on-failure
RestartSec=3

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable rusty_lights
sudo systemctl start rusty_lights

# Check status / logs
sudo systemctl status rusty_lights
journalctl -u rusty_lights -f

MPD-Based Player Integration

The FIFO backend works with any audio player built on MPD, including:

Option A: MPD FIFO (MPD playback only)

Add to /etc/mpd.conf:

audio_output {
    type "fifo"
    name "Visualizer"
    path "/tmp/mpd.fifo"
    format "48000:16:2"
}

Restart MPD: sudo systemctl restart mpd

Config:

[audio]
backend = "fifo"
fifo_path = "/tmp/mpd.fifo"
sample_rate = 48000

Option B: ALSA Loopback (all sources, moOde example)

This captures audio from all sources (MPD, Spotify, AirPlay, etc.) by routing through an ALSA loopback device. Shown here for moOde, but the principle applies to any ALSA-based setup.

  1. Load the loopback module permanently:

    echo "snd-aloop" | sudo tee -a /etc/modules
    sudo modprobe snd-aloop
  2. Enable ALSA Loopback in the moOde Web-UI under System Config.

  3. Config:

    [audio]
    backend = "alsa"
    device = "hw:Loopback,1"
    sample_rate = 48000

Note: The ALSA backend requires the binary to be built with --features alsa.

Architecture

Thread model (3 threads)

  1. Main — CLI (clap), config loading, signal handling
  2. Audio — PipeWire/ALSA/FIFO callback writes to lock-free SPSC ring buffer
  3. Processing — Reads ring buffer → DSP pipeline → effect render → protocol output

Data flow per frame

Audio callback → RingBuffer(SPSC, 8192 samples)
    → FftProcessor(512-pt, Hanning window)
    → MelBank(24 bands, 20-18kHz)
    → Smoother(EMA) + BeatDetector(spectral flux)
    → Effect.render(mel_bands, beat) → RGB buffer
    → util::reorder_rgb() + gamma correction
    → E131/DDP/ArtNet sender (UDP)

Design constraints

  • Zero allocations per frame after initialization — reuse buffers
  • Target: <5% CPU on RPi 4, <150µs per frame
  • No async runtime — simple threading is sufficient
  • Lock-free audio path — no mutexes between audio and processing threads

Effects

Effect Description
energy Maps bass/mid/high energy to RGB across all LEDs
spectrum Frequency bands mapped to LED positions, gradient-colored
scroll Scrolling color history
reactive Beat-triggered flash and decay
pulse Beat-synchronized pulsing
vumeter Classic VU meter display
chromafreq Spectrum across LEDs, energy mapped to hue, attack controls brightness

DSP Tuning

Parameter Effect Range
smoothing LED fade-out speed (release time) 0.0 (instant) - 1.0 (very slow)
beat_sensitivity Beat detection threshold 1.0 (very sensitive) - 3.0 (only strong beats)
brightness Overall LED brightness 0.0 - 1.0
idle_timeout Close output after N minutes of silence 0 (disabled) - any value in minutes

Roadmap

  • LED layouts — Support for matrix, ring, and custom LED arrangements with coordinate mapping (serpentine wiring, 2D/polar coordinates). Enables layout-aware effects like matrix equalizers and radial pulses.
  • Ring topology — Explicit layout = "ring" config so effects can use the ring's continuous topology (radial beats, mirrored from a "top" position). WLED treats rings as linear strips by default, so we need our own config.
  • WLED auto-discoveryFind WLED controller via mDNS, auto-configure LED count and RGB order from the WLED JSON API. Zero-config setup with target = "auto". Remaining: layout (strip/matrix) detection, multi-device discovery.
  • Spectrogram effect — Scrolling frequency-time display across the LED strip using full 257-bin FFT resolution.
  • Multi-band onset effect — Independent beat detection per frequency region (kick, snare, hi-hat) using per-band spectral flux. Qualitative leap beyond single global beat triggers.
  • Performance optimizationsBulk-copy ring buffer, eliminate per-frame allocations in DDP sender, remove redundant RGB↔DMX conversion in processing loop
  • 2D matrix effects — Compute-intensive effects for LED matrices that go far beyond microcontroller capabilities:
    • Fluid Simulation — 2D Navier-Stokes fluid dynamics, beats inject colored vortices, frequency bands control color and viscosity
    • Shockwave Ripples — Beat-triggered concentric waves with interference patterns, frequency maps to color
    • Particle Fireworks — Beat-spawned particles with gravity, bass creates explosions, hi-hat creates sparks
    • Radial Spectrum — Circular frequency display from center outward, beats pulse from the core
    • Audio Terrain — Perspective-rendered 3D heightmap of the frequency spectrum, slowly rotating
    • Plasma Morph — Classic plasma with all parameters modulated by audio analysis
    • Matrix Rain — Beat-triggered drops, tempo controls speed, frequency controls color
    • Game of Life + Audio — Cellular automata seeded by beats, frequency alters evolution rules
  • Scenes/presets — Save and recall effect + parameter combinations
  • Multi-device output — Drive multiple LED controllers simultaneously. Three modes: mirror (same frame to all devices, default for target = "auto"), chain (devices form one continuous strip, requires explicit config), segments (manual LED-range-to-device mapping). MultiSegmentOutput wraps multiple outputs behind the LedOutput trait.

License

This project is licensed under the GNU General Public License v3.0.

About

Audio-reactive LED visualizer written in Rust. Captures audio via PipeWire, ALSA, or FIFO (MPD), runs it through a real-time DSP pipeline (FFT, Mel filterbank, beat detection), and drives LED strips over E1.31/sACN, DDP, or Art-Net. 7 built-in effects, multi-universe support, lock-free audio path. Targets Raspberry Pi with <5% CPU usage.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors