A high-performance, real-time LED control system designed for professional installations, live performances, and embedded deployments. Built with Go backend and React frontend, featuring a sophisticated filtergraph-based processing engine capable of 60 FPS LED control.
This guide follows the complete user journey from installation to creating stunning LED displays on embedded hardware like Raspberry Pi or Orange Pi.
Using Raspberry Pi Imager (recommended):
- Download Raspberry Pi Imager
- Select your device:
- Raspberry Pi 3B: Use
Raspberry Pi OS Lite (64-bit) - Raspberry Pi 4: Use
Raspberry Pi OS Lite (64-bit) - Orange Pi 3: Use appropriate Orange Pi OS image
- Raspberry Pi 3B: Use
- Configure before flashing:
- Hostname:
deskpi(ororangepi3) - Username:
pi - Password: Your choice
- Enable SSH: β Use password authentication
- WiFi: Configure your network
- Locale: Set your timezone
- Hostname:
After booting the device:
# Test SSH connection
ssh pi@deskpi.local
# or
ssh pi@orangepi3.local
# Set up SSH key for passwordless access
ssh-copy-id pi@deskpi.local
# Add convenient alias (optional)
echo 'alias deskpi="ssh pi@deskpi.local"' >> ~/.zshrc
source ~/.zshrcSSH into your device and prepare the system:
# Update system
sudo apt update && sudo apt upgrade -y
# Create dedicated LED controller user (for security)
sudo useradd -m -s /bin/bash -G gpio,spi,i2c ledctl
sudo passwd ledctl
# Set up SPI permissions (enable SPI and configure group access)
# Enable SPI interface
echo 'dtparam=spi=on' | sudo tee -a /boot/firmware/config.txt
# Create SPI group if it doesn't exist and add user
sudo groupadd -f spi
sudo usermod -a -G spi ledctl
# Configure udev rules for device permissions
sudo tee /etc/udev/rules.d/99-led-controller.rules << 'EOF'
# GPIO and SPI access for LED controller
SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
KERNEL=="gpiomem", GROUP="gpio", MODE="0660"
KERNEL=="i2c-[0-9]*", GROUP="i2c", MODE="0660"
EOF
# Reload udev rules
sudo udevadm control --reload-rules && sudo udevadm trigger
# Install required system dependencies
sudo apt install -y curl wget unzip systemd
# Reboot to apply SPI and GPIO configuration
sudo reboot
# Check SPI is available:
ls /dev | grep spi
# Check SPI bufsize (defaults to 4096):
cat /sys/module/spidev/parameters/bufsiz
# Adjust SPI bufsize:
# Add spidev.bufsiz=32768 to /boot/firmware/cmdline.txt (modify existing line, don't append new line)
sudo sed -i 's/$/ spidev.bufsiz=32768/' /boot/firmware/cmdline.txt
# Verify the change (entire file should be one line):
cat /boot/firmware/cmdline.txt
# Reboot again
sudo reboot
# Check SPI bufsize is now increased:
cat /sys/module/spidev/parameters/bufsizAfter reboot, connect as the LED controller user:
# SSH as the dedicated user
ssh pi@deskpi.local
su ledctl
cd ~
# Create working directory
mkdir -p ~/ledserver
cd ~/ledserverMethod 1: Download via wget/curl (Production)
TODO: repository needs to be public
# Download binary for your architecture
# For Raspberry Pi 3B (ARM):
wget https://github.com/segfault16/modular-led-controller-workstation-v2/releases/latest/download/modular-led-controller-workstation-v2-linux-arm
# For Raspberry Pi 4 / Orange Pi 3 (ARM64):
wget https://github.com/segfault16/modular-led-controller-workstation-v2/releases/latest/download/modular-led-controller-workstation-v2-linux-arm64
# For x86_64 systems:
wget https://github.com/segfault16/modular-led-controller-workstation-v2/releases/latest/download/modular-led-controller-workstation-v2-linux-amd64
# Make executable
chmod +x modular-led-controller-workstation-v2-*
mv modular-led-controller-workstation-v2-* modular-led-controller-workstation-v2
# Test run
./modular-led-controller-workstation-v2# Use docker to build the binary
task docker:build:linux-arm64
# Copy all binaries
task remote:copy TARGET_HOST=pi@deskpi.local TARGET_ARCH=linux-arm64ssh pi@deskpi.local
chmod +x modular-led-controller-workstation-v2
./modular-led-controller-workstation-v2# Navigate to installation directory
cd ~/ledserver
# Start the LED controller
./ledcontroller
# Access web interface from your computer:
# - http://deskpi.local:3000
# - http://orangepi3.local:3000
# - http://[device-ip]:3000# Create systemd service file
sudo tee /etc/systemd/system/ledcontroller.service << 'EOF'
[Unit]
Description=LED Controller Service
After=network.target
[Service]
Type=simple
User=ledctl
Group=ledctl
WorkingDirectory=/home/ledctl/ledserver
ExecStart=/home/ledctl/ledserver/ledcontroller
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# Security settings
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/home/ledctl/ledserver
[Install]
WantedBy=multi-user.target
EOF
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable ledcontroller
sudo systemctl start ledcontroller
# Check status
sudo systemctl status ledcontrollerThe system supports two distinct output methods for driving LEDs:
Best for: Single-channel, moderate LED counts (up to ~1000 LEDs)
Uses throttled SPI timing to directly drive LED protocols from the Raspberry Pi:
Supported LED Types:
- WS2812B (NeoPixel) - 5V, single data line
- WS2815 (12V addressable) - 12V, single data line
- APA102 (DotStar) - Separate clock and data lines
- Custom SPI protocols - Configurable timing
Characteristics:
- SPI speed throttled to match LED protocol timing (~3.8 MHz for WS2812)
- Direct bit-banging of LED data protocols
- Limited to single output channel per SPI bus
- Suitable for installations up to ~1000 LEDs
Performance Limits:
Based on Hz = 4.0 / 1.05e-6 β 3.81 MHz SPI frequency:
- Each LED requires 96 SPI bits (24 RGB bits Γ 4 SPI bits encoding)
- 60 FPS maximum: ~660 LEDs (3.81MHz Γ· 96 bits Γ· 60fps = 659 LEDs)
- 30 FPS maximum: ~1,320 LEDs (3.81MHz Γ· 96 bits Γ· 30fps = 1,319 LEDs)
- Real-world limits: ~500-800 LEDs at 60 FPS (accounting for overhead)
Best for: Multi-channel, high LED counts (1000+ LEDs per channel)
Uses full-speed SPI communication with ESP32 microcontrollers for multi-channel output:
Characteristics:
- Full-speed SPI communication (up to 80 MHz)
- ESP32 handles LED protocol timing on multiple channels
- Supports thousands of LEDs per channel
- Multiple independent output channels
- Real-time data streaming to ESP32
- ESP32 firmware manages hardware-specific LED output
Performance Limits:
- Per Channel: 5,000+ LEDs at 60 FPS (limited by ESP32 processing power)
- Total System: 10,000+ LEDs across multiple channels
- Frame Rate: Consistent 60 FPS even with high LED counts
- Latency: Low latency due to dedicated hardware processing
Device Configuration:
{
"device": "candy",
"candyServer": "esp32.local:7890",
"numPixel": 2000,
"multiChannel": true
}Raspberry Pi SPI Connections:
- MOSI (GPIO 10) β LED Data Pin
- SCLK (GPIO 11) β LED Clock Pin (for 4-wire LEDs like APA102)
- GND (Pin 6, 9, 14, 20, 25, 30, 34, 39) β LED Ground
Raspberry Pi to ESP32:
- MOSI (GPIO 10) β ESP32 SPI MOSI
- SCLK (GPIO 11) β ESP32 SPI SCLK
- CS (GPIO 8) β ESP32 SPI CS
- GND β ESP32 GND
ESP32 to LEDs:
- Multiple GPIO pins configured for LED output channels
- Each channel drives separate LED strips independently
- Hardware-optimized LED protocol generation
Power Supply (CRITICAL SAFETY):
- NEVER connect Raspberry Pi 5V to LED power
- Use separate dedicated 5V/12V power supply for LEDs
- ONLY connect grounds together (Pi GND β LED GND β Power Supply GND)
- Power supply must match LED strip voltage (5V for WS2812, 12V for WS2815)
Basic Safety:
- Always use external power supply for LED strips (any length)
- Add 470Ξ© resistor in series with data line
- Use logic level shifter for 5V LED strips (3.3V β 5V)
- Shared ground is essential - never skip the ground connection
Once the LED controller is running, access the web interface from any device on your network:
# Open in browser:
http://deskpi.local:3000
http://orangepi3.local:3000
http://[device-ip]:3000- Device Configuration: Set up your LED hardware and SPI settings
- Project Management: Create or load LED projects
- Scene Creation: Design visual effects and transitions
- Real-time Control: Adjust parameters and trigger effects
Projects are JSON configuration files containing complete LED setups including scenes, effects, and device configurations.
# Using the built-in task system
task remote:init-project TARGET_HOST=orangepi3.local PROJECT=your-project-name
# This clones from https://github.com/segfault16/p2_desk_presets by default-
Create project directory:
mkdir -p ~/.ledserver/projects/my-project -
Add project files:
File Path: ~/.ledserver/projects/[project-name]/[project-name].json
Complete Project Structure:
// ~/.ledserver/projects/desk-setup/desk-setup.json
{
"ID": "desk-setup",
"Title": "Desk LED Setup",
"Description": "Multi-zone desk lighting with audio reactivity",
"ActiveSceneID": 2,
"Scenes": {
"0": {
"Metadata": {
"Title": "Static Colors",
"Group": "Basic",
"SceneID": 0,
"NumSlots": 2,
"DeviceSlotTags": {
"0": ["main", "desk"],
"1": ["ambient", "background"]
},
"DeviceSlotMapping": {
"0": 0,
"1": 1
}
},
"SceneSlots": {
"0": {
"nodes": [
{
"UUID": "static-color-node",
"Effect": {
"NumInputChannels": 0,
"NumOutputChannels": 1,
"R": {"Orig": 255},
"G": {"Orig": 128},
"B": {"Orig": 0}
},
"EffectType": "colors.staticRGBColor"
},
{
"UUID": "output-node",
"Effect": {
"NumInputChannels": 1,
"NumOutputChannels": 0,
"Brightness": {"Orig": 0}
},
"EffectType": "effects.LedOutput"
}
],
"connections": [
{
"from_node_uid": "static-color-node",
"from_node_channel": 0,
"to_node_uid": "output-node",
"to_node_channel": 0,
"uid": "connection-1"
}
],
"modulationSources": [...],
"modulation": [...]
}
}
},
"1": {
"Metadata": {
"Title": "Audio Reactive",
"Group": "Dynamic",
"SceneID": 1
}
}
}
}- Access Projects page in web interface
- Use Import Project to upload JSON files
- Supports both individual scenes and complete projects
~/.ledserver/
βββ projects/
β βββ my-project/
β β βββ project.json # Main project configuration
β β βββ scene1.json # Individual scenes
β β βββ assets/ # Project assets (images, etc.)
β βββ another-project/
βββ config.json # Global configuration
βββ device-configs.json # Hardware configurations
Device configuration is critical for proper LED output. The system supports multiple output types and complex routing scenarios.
- Access Configuration Page in web interface
- Define Device Configurations:
File Path: ~/.ledserver/configuration.json
Object Path: configuration.json β DeviceConfigs β DeviceConfigs β [config-name]
{
"DeviceConfigs": {
"DeviceConfigs": {
"desk-setup": [
{
"Name": "Main Strip",
"NumPixel": 880,
"Device": "spi",
"ScaleBrightness": 1.0,
"Order": 0,
"Primary": true
}
],
"segmented-desk": [
{
"Name": "Right Side",
"NumPixel": 120,
"Device": "virtual",
"VirtualReference": "desk-setup",
"VirtualStartIndex": 0,
"Tags": ["right", "main"],
"Order": 0
},
{
"Name": "Center Monitor",
"NumPixel": 200,
"Device": "virtual",
"VirtualReference": "desk-setup",
"VirtualStartIndex": 120,
"Tags": ["center", "monitor"],
"Order": 1
},
{
"Name": "Left Side",
"NumPixel": 120,
"Device": "virtual",
"VirtualReference": "desk-setup",
"VirtualStartIndex": 320,
"Tags": ["left", "main", "reverse"],
"Order": 2
}
]
}
}
}Object Path: configuration.json β DeviceConfigs β DeviceConfigs β [config-name] β [device-index]
{
"Name": "High Performance Strip",
"Device": "ws2812SPI",
"NumPixel": 500
}For high-performance multi-channel setups where timing determines LED stripe length:
Object Path: configuration.json β DeviceConfigs β DeviceConfigs β [config-name] β [device-index]
{
"Name": "ESP32 Multi-Channel",
"Device": "candy",
"CandyServer": "192.168.1.100:7890",
"NumPixel": 1000
}For complex installations with multiple physical segments:
Object Path: configuration.json β DeviceConfigs β DeviceConfigs β [config-name] β [device-index]
[
{
"Name": "Segment 1",
"Device": "virtual",
"NumPixel": 150,
"VirtualReference": "main-strip",
"VirtualStartIndex": 0
},
{
"Name": "Segment 2",
"Device": "virtual",
"NumPixel": 150,
"VirtualReference": "main-strip",
"VirtualStartIndex": 150
}
]The system uses slot tags for flexible device routing:
Object Path: configuration.json β DeviceConfigs β DeviceConfigs β [config-name] β [device-index]
{
"Name": "Ceiling Lights",
"Device": "ws2812SPI",
"NumPixel": 200,
"SlotTag": "ceiling-lights",
"Order": 1
}Slot tags allow scenes to target specific hardware without hardcoding device indices.
Understanding Scenes and Slots is crucial - This is the most complex part of the system, enabling flexible multi-zone LED control with tag-based routing.
Project
βββ Scene 0: "Static Colors"
β βββ Scene Slot 0 β [Filtergraph] β Left LED Bar
β βββ Scene Slot 1 β [Filtergraph] β Right LED Bar
βββ Scene 1: "Audio Reactive"
β βββ Scene Slot 0 β [Audio VU Meter] β Left LED Bar
β βββ Scene Slot 1 β [Audio Spectrum] β Right LED Bar
β βββ Scene Slot 2 β [Audio Visualizer] β Desk Strip
βββ Scene 2: "Ambient"
βββ Scene Slot 0 β [Color Wheel] β All Devices
Scene Slots enable independent control of different LED zones within a single scene. Each slot contains its own filtergraph that can drive different visual effects.
Physical Setup:
βββββββββββββββββββββββββββββββββββββββ
β [Left Bar] [Monitor] [Right Bar] β β Individual LED strips
β [Desk Strip] β β Under-desk lighting
βββββββββββββββββββββββββββββββββββββββ
Scene Slot Mapping:
β’ Scene Slot 0 β Left Bar (VU Meter visualization)
β’ Scene Slot 1 β Right Bar (Audio spectrum)
β’ Scene Slot 2 β Desk Strip (Color wheel effect)
β’ Scene Slot 3 β Monitor (Ambient breathing)
- Scene Switches: Located above the filtergraph visualization
- Scene Slots: Each row below the visualization represents one scene slot
- Column Selection: Same filtergraph can drive multiple slots by selecting the same column
- Live Preview: Visual feedback shows which devices are active
[Scene: Audio Reactive] [Scene: Static] [Scene: Ambient] β Scene switches
Slot 0: [Audio Input] β [VU Meter] β [LED Output] β Row = Scene Slot
ββββββββββββββββ Column 1 βββββββββββββββ
Slot 1: [Audio Input] β [Spectrum] β [LED Output] β Independent slot
ββββββββββββββββ Column 1 βββββββββββββββ β Same input column
Slot 2: [Color Wheel] βββββββββββ [LED Output] β Different effect
βββββ Column 2 βββββββββββββββββββββ
The system uses flexible tag-based routing to map scene slots to physical devices without hardcoding connections.
1. Device Configuration with Tags
File Path: ~/.ledserver/configuration.json
{
"DeviceConfigs": {
"DeviceConfigs": {
"desk-setup": [
{
"Name": "Left LED Bar",
"Device": "ws2812SPI",
"NumPixel": 120,
"SlotTag": "left-bar",
"Tags": ["left", "bar", "audio-reactive"]
},
{
"Name": "Right LED Bar",
"Device": "virtual",
"VirtualReference": "desk-setup",
"VirtualStartIndex": 120,
"NumPixel": 120,
"SlotTag": "right-bar",
"Tags": ["right", "bar", "audio-reactive"]
},
{
"Name": "Under Desk",
"Device": "virtual",
"VirtualReference": "desk-setup",
"VirtualStartIndex": 240,
"NumPixel": 200,
"SlotTag": "desk-ambient",
"Tags": ["ambient", "desk", "background"]
}
]
}
}
}2. Scene Slot Tag Assignment
File Path: ~/.ledserver/projects/[project-name]/[project-name].json
{
"Scenes": {
"1": {
"Metadata": {
"Title": "Audio Reactive Desk",
"DeviceSlotTags": {
"0": ["left", "audio-reactive"], // Slot 0 targets left bar
"1": ["right", "audio-reactive"], // Slot 1 targets right bar
"2": ["ambient", "background"] // Slot 2 targets ambient
},
"DeviceSlotMapping": {
"0": 0, // Device slot 0 β Scene slot 0
"1": 1, // Device slot 1 β Scene slot 1
"2": 2 // Device slot 2 β Scene slot 2
}
}
}
}
}3. Runtime Routing Resolution The system automatically matches devices to scene slots:
Routing Logic:
Device "Left LED Bar" has tags: ["left", "bar", "audio-reactive"]
Scene Slot 0 requests tags: ["left", "audio-reactive"]
β MATCH: Left LED Bar connects to Scene Slot 0
Device "Right LED Bar" has tags: ["right", "bar", "audio-reactive"]
Scene Slot 1 requests tags: ["right", "audio-reactive"]
β MATCH: Right LED Bar connects to Scene Slot 1
Device "Under Desk" has tags: ["ambient", "desk", "background"]
Scene Slot 2 requests tags: ["ambient", "background"]
β MATCH: Under Desk connects to Scene Slot 2
When devices don't have explicit SlotTag values, the system auto-generates them:
// From serverConfig.go adjustSlotTags()
// Devices without SlotTag get: "unnamed_0", "unnamed_1", etc.
d.SlotTag = fmt.Sprintf("unnamed_%v", curCnt)File Path: ~/.ledserver/projects/[project-name]/[project-name].json
New scenes automatically get:
{
"Metadata": {
"DeviceSlotTags": {
"0": ["unnamed_0", "unnamed_1", "unnamed_2"] // All devices
},
"DeviceSlotMapping": {
"0": 0 // Single slot drives all devices
}
}
}Scenes can target different hardware setups using tag ranges:
{
"DeviceSlotTags": {
"0": ["main", "primary"], // Works with any "main" device
"1": ["secondary", "accent"], // Works with any "secondary" device
"2": ["ambient", "background"] // Works with ambient lighting
}
}Example: Same scene works on different setups
Setup A (Desk): Setup B (Room): Setup C (Stage):
- Main Strip - Ceiling Lights - Front Wash
- Side Lights - Wall Washers - Back Lights
- Ambient - Floor Strips - Effect Lights
Virtual devices enable complex routing within single physical strips:
{
"Name": "Left Section",
"Device": "virtual",
"VirtualReference": "main-strip", // References physical device
"VirtualStartIndex": 0, // Starts at pixel 0
"NumPixel": 150, // Uses first 150 pixels
"SlotTag": "left-zone"
}Device tags also control pixel transformations:
{
"Tags": ["reverse", "left", "audio-reactive"],
// "reverse" β Pixels flow right-to-left
// "reverse-first-half" β Only first half reversed
// "reverse-second-half" β Only second half reversed
}{
"ScaleBrightness": 0.7, // 70% brightness
"ScaleBrightnessActive": true, // Enable scaling
"Tags": ["bright-zone"]
}- No devices light up: Check tag matching between scene slots and devices
- Wrong devices active: Verify
DeviceSlotMappingin scene metadata - Missing effects: Ensure scene slots have filtergraphs with LED output nodes
# Check active device configuration
curl http://localhost:3000/api/config/device-configs
# Verify scene slot assignments
curl http://localhost:3000/api/projects/[project-id]/scenes/[scene-id]- Devices without
SlotTagget auto-generated tags:unnamed_0,unnamed_1... - Scenes without
DeviceSlotTagsget all device tags assigned to slot 0 - Empty scenes get default LED output node in slot 0
This flexible routing system enables complex multi-zone installations while maintaining compatibility across different hardware setups through intelligent tag-based matching.
The core of the system is a Directed Acyclic Graph (DAG) processing engine that processes LED data in real-time.
File Path: ~/.ledserver/projects/[project-name]/[project-name].json
Object Path: project.json β Scenes β [scene-id] β SceneSlots β [slot-id] β nodes
{
"SceneSlots": {
"0": {
"nodes": [
{
"UUID": "audio-input-main",
"Effect": {
"NumInputChannels": 0,
"NumOutputChannels": 2,
"AutoGain": {"Val": true},
"AutoGainMax": {"Orig": 10.0},
"AutoGainTime": {"Orig": 10.0}
},
"EffectType": "effects.AudioInput"
},
{
"UUID": "vu-meter-left",
"Effect": {
"NumInputChannels": 1,
"NumOutputChannels": 1,
"Sensitivity": {"Orig": 1.0},
"PeakHold": {"Orig": 0.8},
"Falloff": {"Orig": 0.95}
},
"EffectType": "audioreactive.vuMeter"
},
{
"UUID": "output-desk",
"Effect": {
"NumInputChannels": 1,
"NumOutputChannels": 0,
"Brightness": {"Orig": 0}
},
"EffectType": "effects.LedOutput"
}
],
"connections": [
{
"from_node_uid": "audio-input-main",
"from_node_channel": 0,
"to_node_uid": "vu-meter-left",
"to_node_channel": 0,
"uid": "connection-1"
},
{
"from_node_uid": "vu-meter-left",
"from_node_channel": 0,
"to_node_uid": "output-desk",
"to_node_channel": 0,
"uid": "connection-2"
}
]
}
}
}- Backward Propagation: Graph analysis determines required pixel counts
- Forward Processing: Real-time pixel data flows through nodes
- Buffer Management: Efficient memory allocation for 60 FPS operation
- Parallel Execution: Multi-threaded processing for performance
AudioInput(300px) β Mixer(300px) β LedOutput(300px)
RainbowGen(300px) β
Create pixel data from scratch:
- Static RGB Color (
colors.staticRGBColor): Solid colors with RGB control - Color Wheel (
colors.colorWheel): Rotating rainbow patterns with speed control - Static Blob (
generative.staticBlob): Procedural blob patterns - Swimming Pool (
generative.swimmingPool): Ripple effects simulation - Generate Waves (
generative.generateWaves): Sine wave patterns - Falling Stars (
generative.fallingStars): Meteor-like effects - Pendulum (
generative.pendulum): Swinging light effects
Transform existing pixel data:
- After Glow (
effects.afterGlow): Persistence/trail effects with decay - Mirror (
effects.mirror): Mirror patterns at specified points - Shift (
effects.shift): Move patterns along the strip - Swing (
effects.swing): Pendulum-like movement modifier - Shapes (
effects.shapes): Geometric shape overlay effects - Spring Combine (
effects.springCombine): Physics-based combination
Respond to audio input:
- Audio Input (
effects.AudioInput): Live audio capture with auto-gain - VU Meter (
audioreactive.vuMeter): Classic level visualization - Spectrum (
audioreactive.spectrum): Frequency analysis display - Dual Spectrum (
audioreactive.dualSpectrum): Stereo spectrum analyzer - Moving Light (
audioreactive.movingLight): Audio-driven light movement - Bonfire (
audioreactive.bonfire): Flame-like audio response - Falling Stars (
audioreactive.fallingStars): Audio-triggered meteors - Blink (
audioreactive.blink): Beat-synchronized flashing
Advanced color manipulation:
- Color Blend (
colors.colorBlend): Multi-layer color mixing - RGB to HSV (
colors.rgbToHSV): Color space conversion - HSV to RGB (
colors.hsvToRGB): Hue/saturation processing - Interpolate HSV (
colors.interpolateHSV): Smooth color transitions
System and control functions:
- LED Output (
effects.LedOutput): Hardware output interface - Append (
effects.append2,effects.append4, etc.): Multi-channel concatenation - Router: Signal routing and distribution
Each effect exposes modulatable parameters:
Object Path: project.json β Scenes β [scene-id] β SceneSlots β [slot-id] β nodes β [node-index] β Effect
{
"Effect": {
"NumInputChannels": 0,
"NumOutputChannels": 1,
"Speed": {
"Orig": 0.3,
"Min": 0.0,
"Max": 2.0,
"Step": 0.1
},
"Saturation": {
"Orig": 1.0,
"Min": 0.0,
"Max": 1.0,
"Step": 0.01
},
"Brightness": {
"Orig": 0.8,
"Min": 0.0,
"Max": 1.0,
"Step": 0.01
}
},
"EffectType": "colors.colorWheel"
}Audio-Reactive Effect Parameters:
Object Path: project.json β Scenes β [scene-id] β SceneSlots β [slot-id] β nodes β [node-index] β Effect
{
"Effect": {
"NumInputChannels": 1,
"NumOutputChannels": 1,
"Sensitivity": {
"Orig": 1.0,
"Min": 0.1,
"Max": 5.0,
"Step": 0.1
},
"PeakHold": {
"Orig": 0.8,
"Min": 0.0,
"Max": 1.0,
"Step": 0.01
},
"Falloff": {
"Orig": 0.95,
"Min": 0.5,
"Max": 0.99,
"Step": 0.01
}
},
"EffectType": "audioreactive.vuMeter"
}Modifier Effect Parameters:
Object Path: project.json β Scenes β [scene-id] β SceneSlots β [slot-id] β nodes β [node-index] β Effect
{
"Effect": {
"NumInputChannels": 1,
"NumOutputChannels": 1,
"Decay": {
"Orig": 0.95,
"Min": 0.5,
"Max": 0.99,
"Step": 0.01
},
"Threshold": {
"Orig": 0.1,
"Min": 0.0,
"Max": 1.0,
"Step": 0.01
}
},
"EffectType": "effects.afterGlow"
}The modulation system enables dynamic parameter control and automation.
- LFO (Low Frequency Oscillator): Sine, triangle, square waves
- Audio Reactive: Beat, RMS, frequency bands
- Manual Control: MIDI controllers, web interface
- Time-based: Linear ramps, step sequences
File Path: ~/.ledserver/projects/[project-name]/[project-name].json
Object Path: project.json β Scenes β [scene-id] β SceneSlots β [slot-id] β modulation
{
"SceneSlots": {
"0": {
"modulation": [
{
"modulation_source_id": "95453dd7-47b8-453a-bc4d-6634f61650a8",
"target_node_id": "output-main",
"target_param": "Brightness",
"amount": 1.0,
"inverted": false,
"uid": "brightness-modulation-1"
},
{
"modulation_source_id": "d075134f-2d87-436a-ab57-6b3037d00f04",
"target_node_id": "static-color-node",
"target_param": "R",
"amount": 1.0,
"inverted": false,
"uid": "color-red-modulation"
}
],
"modulationSources": [
{
"UUID": "95453dd7-47b8-453a-bc4d-6634f61650a8",
"ModulationSourceType": "modulations.externalLinearController",
"ModulationSource": {
"Amount": 1.0,
"Controller": "Brightness"
}
},
{
"UUID": "d075134f-2d87-436a-ab57-6b3037d00f04",
"ModulationSourceType": "modulations.externalColourAController",
"ModulationSource": {
"ControllerR": null,
"ControllerG": null,
"ControllerB": null
}
}
]
}
}
}- MIDI Integration: Hardware controllers for live performance
- Web Interface: Real-time parameter adjustment
- Bluetooth: Wireless control via custom protocol
- Audio Reactive: Automatic response to music
{
"maxSPISpeedMHz": 4.0, // SPI bus speed
"targetFPS": 60, // Rendering target
"audioBufferSize": 512 // Audio latency vs quality
}- Buffer Pooling: Efficient pixel buffer reuse
- Garbage Collection: Optimized for real-time performance
- Channel Optimization: Minimize unnecessary allocations
For multi-channel high-performance setups:
{
"device": "candy",
"candyServer": "esp32.local:7890",
"numPixel": 2000,
"multiChannel": true,
"timingCritical": true
}# Enable SPI in /boot/firmware/config.txt (or /boot/config.txt on older systems)
dtparam=spi=on
# Adjust SPI buffer size for large LED counts (IMPORTANT: keep cmdline.txt on one line)
# Check current buffer size:
cat /sys/module/spidev/parameters/bufsiz
# Add spidev.bufsiz=32768 to /boot/firware/cmdline.txt (modify existing line, don't append new line)
sudo sed -i 's/$/ spidev.bufsiz=32768/' /boot/firware/cmdline.txt
# Verify the change (entire file should be one line):
cat /boot/firware/cmdline.txt- Automatic device discovery on local network
- Service advertisement for remote management
- Zeroconf integration for plug-and-play setup
- SSH Access: Command line management
- Web Interface: Full remote control
- Update System: Over-the-air updates
- Real-time FPS Monitoring: Performance metrics
- Device Status: Hardware connection status
- Scene Control: Quick scene switching
- Parameter Control: Live effect adjustment
- Project Browser: Visual project selection
- Import/Export: Project file management
- Version Control: Project history tracking
- Asset Management: Image and resource handling
- Device Setup: Hardware configuration wizard
- Audio Settings: Input device selection
- Network Settings: Connectivity configuration
- Update Management: System updates
- Real-time Preview: Visual feedback before deployment
- Pixel Visualization: Individual LED state display
- Performance Metrics: Processing time analysis
- Debug Information: Detailed system status
- System Audio: Computer audio output
- Microphone: Live audio capture
- Audio Streaming: Network audio sources
- File Playback: Pre-recorded audio
- FFT Analysis: Frequency domain processing
- Beat Detection: Rhythm extraction
- RMS Calculation: Audio level analysis
- Multi-channel: Stereo and surround support
{
"audioReactive": {
"fftBands": 64,
"beatSensitivity": 0.7,
"frequencyRange": {"low": 20, "high": 20000},
"smoothing": 0.8
}
}The system includes automated update capabilities for production deployments.
- Signed Updates: RSA cryptographic verification
- Multi-platform: Updates for all supported architectures
- Atomic Updates: Safe binary replacement
- Rollback Support: Version history management
- HTTP/HTTPS: Remote update servers
- Local Files: USB or network storage
- Version Archives: Historical version access
- Version Check: Compare current vs available
- Download: Fetch signed binary
- Verification: Cryptographic signature check
- Installation: Atomic binary replacement
- Restart: Automatic service restart
- Web Interface: Manual update triggers
- MIDI Controller: Hardware-based updates
- Automatic: Scheduled update checks
- Emergency: Force update capabilities
Note: This section is for developers who want to modify the software. Production users should use the binary installation above.
- VS Code with Remote SSH extension
- SSH access to your embedded device configured
- Basic knowledge of Go and TypeScript
For the best development experience, use VS Code with Remote SSH extension to develop directly on the embedded hardware:
Step 1: Connect via VS Code Remote SSH
- Install Remote - SSH extension in VS Code
- Open Command Palette (
Cmd+Shift+P/Ctrl+Shift+P) - Run
Remote-SSH: Connect to Host... - Enter:
ledctl@deskpi.local(or your device hostname) - VS Code will connect and you can develop directly on the hardware
Step 2: Install Development Environment on Device
# Install Go 1.24+ (in VS Code terminal on remote device)
wget -c https://golang.org/dl/go1.24.5.linux-arm64.tar.gz
sudo tar -C /usr/local -xzf go1.24.5.linux-arm64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
go version
# Install Task (build tool)
sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
# Install Node.js for frontend development
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejsStep 3: Clone and Setup Project
# In VS Code integrated terminal on remote device
git clone https://github.com/segfault16/modular-led-controller-workstation-v2.git
cd modular-led-controller-workstation-v2
# Install Go dependencies
go mod download
# Install frontend dependencies
cd server && npm install && cd ..
# Generate API bindings
task local:protogen
# Build and run
task local:build
./build/local/modular-led-controller-workstation-v2- Go 1.24+: High-performance backend
- gRPC: API communication layer
- Protocol Buffers: Efficient serialization
- Real-time Processing: 60 FPS pixel processing
// Effect interface
type Effect interface {
Process()
Update(ctx context.Context, dt float64)
GetNumInputPixel(channel int) int
GetNumOutputPixel(channel int) int
}
// Device interface
type Device interface {
Show(pixels *mat.Dense) error
GetMaxPixels() int
}# Code generation
task local:protogen
# Backend development
go run main.go --config-location ./dev-config
# Testing
go test ./...
# Benchmarking
task benchmark- React 18: Modern UI framework
- TypeScript: Type-safe development
- Vite: Fast development server
- Material-UI: Component library
- gRPC-Web: Backend communication
cd server
npm install
npm run start # Development server on :3001# Production build
task build:frontend
# This creates optimized build in server/dist/
# Which gets embedded in Go binary via go:embed- Graph Editor: Visual filtergraph editing
- Parameter Controls: Real-time effect adjustment
- Project Manager: Project file handling
- Device Configuration: Hardware setup wizard
- Configuration: System and device configuration
- Project: Project and scene management
- Filtergraph: Graph manipulation
- Stats: Performance monitoring
- Preview: Visual feedback
service Configuration {
rpc GetServerConfiguration(Empty) returns (ServerConfiguration);
rpc UpdateServerConfiguration(ServerConfiguration) returns (ServerConfiguration);
rpc CheckForUpdates(Empty) returns (UpdateCheckResponse);
rpc UpdateNow(Empty) returns (Empty);
}# Run all tests
go test ./...
# Specific package tests
go test ./effects
go test ./filtergraph- Hardware Mocking: Device simulation
- End-to-end: Full system testing
- Performance: FPS and latency testing
- Device Testing: Physical LED verification
- Audio Testing: Live audio processing
- Network Testing: Remote connectivity
- Edit code directly in VS Code with full IntelliSense
- Use integrated terminal for build commands
- Live debugging with Go extension
- Frontend development with hot reload:
cd server && npm run start - Access web interface at
http://deskpi.local:3000
- Go (golang.go)
- TypeScript and JavaScript Language Features
- ESLint
- GitLens
- Remote - SSH
- Fork the repository
- Create feature branch
- Follow Go and TypeScript best practices
- Add tests for new functionality
- Submit pull request
- Go: Follow
gofmtandgolangci-lint - TypeScript: ESLint configuration provided
- Testing: Maintain test coverage
- Documentation: Update README for new features
# Update system
sudo apt update && sudo apt upgrade -y
# Enable SPI
sudo raspi-config
# Interface Options β SPI β Enable
# Install dependencies
sudo apt install -y systemd avahi-daemon# Similar to Raspberry Pi
sudo apt update && sudo apt upgrade -y
# Enable SPI in device tree
# (Device-specific configuration)[Unit]
Description=goLEDgo LED Controller
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/ledserver
ExecStart=/home/pi/ledserver/modular-led-controller-workstation-v2
Restart=always
RestartSec=5
Environment=HOME=/home/pi
[Install]
WantedBy=multi-user.target# mDNS configuration
sudo systemctl enable avahi-daemon
# Firewall (if needed)
sudo ufw allow 3000/tcp # Web interface
sudo ufw allow 22/tcp # SSH# Deploy to remote device
task remote:deploy TARGET_HOST=orangepi3.local TARGET_ARCH=linux-arm64
# Initialize projects
task remote:init-project TARGET_HOST=orangepi3.local- System Logs:
journalctl -u goledgo -f - Performance: Built-in FPS monitoring
- Remote Access: Web interface + SSH
# Add user to SPI group
sudo usermod -a -G spi $USER
# Or run with elevated permissions
sudo setcap 'cap_net_raw,cap_net_admin+eip' ./modular-led-controller-workstation-v2# List available audio devices
./modular-led-controller-workstation-v2 --list-audio-devices
# Specify audio device
./modular-led-controller-workstation-v2 --audio-device 2- Check FPS: Monitor real-time FPS in web interface
- Reduce LED Count: Lower pixel count for testing
- SPI Speed: Adjust
maxSPISpeedMHzin configuration - CPU Usage: Monitor system resources
# Check service status
sudo systemctl status goledgo
# Check network interface
ip addr show
# Test web interface
curl http://localhost:3000# Enable verbose logging
./modular-led-controller-workstation-v2 --log-level debug
# No configuration mode (for testing)
./modular-led-controller-workstation-v2 --no-config --num-pixel 100# Backup current config
cp ~/.ledserver/config.json ~/.ledserver/config.json.backup
# Start with default configuration
./modular-led-controller-workstation-v2 --no-config# Emergency stop
sudo systemctl stop goledgo
# Manual binary replacement
# (for update failures)[License information would go here]
- periph.io: SPI and hardware interface library
- go-selfupdate: Update system foundation
- React: Frontend framework
- gRPC: High-performance API layer
# Production deployment
./modular-led-controller-workstation-v2
# Development mode
task local:build && ./build/local/modular-led-controller-workstation-v2
# Update check
curl http://localhost:3000/api/config/updates
# Service control
sudo systemctl {start|stop|restart|status} goledgo- Web Interface: 3000
- gRPC API: 50051
- MIDI Bridge: Various (auto-detected)
- Global Config:
~/.ledserver/config.json - Projects:
~/.ledserver/projects/ - Device Config:
~/.ledserver/device-configs.json
This comprehensive guide covers the complete user journey from initial installation through advanced development scenarios, providing both newcomers and experienced developers with the information needed to successfully deploy and customize the goLEDgo system.