This program controls LED lighting on Ultimarc LED controllers. The software consists of a C playback engine that communicates via USB to the PAC controller, a C based RGBCommander config file format converter, a small C webserver that serves up the RetroPac animation editor that's written in Nuxt (Vue).
Technically this can run on other Raspberry Pi based game systems, making and installing are all the same. You just need to have similar scripts that run when you start up a rom, end a rom and start up Emulation Station or other emulation software and pass certain info.
| Device | LED Channels | RGB LEDs | Intensity Control | Hardware Features |
|---|---|---|---|---|
| I-PAC Ultimate I/O | 96 | 32 | 256 levels | ✓ Fade |
| PacLED64 | 64 | 21 | 256 levels | ✓ Fade, Flash, Scripting |
| NanoLED | 60 | 20 | 256 levels | ✓ Fade, Flash, Scripting |
| PacDrive | 16 | 5 | On/Off only | - |
| U-HID | 16 | 5 | On/Off only | - |
| USB Button | 3 (RGB) | 1 | 256 levels | - |
- Automatically lights up arcade buttons based on game controls
- LED animations for attract mode (rainbow, breathing, chase, sparkle, color cycle)
- Custom animations - Create your own animation sequences in JSON
- Multiple controller support - Control multiple Ultimarc controllers simultaneously
- Multi-device support - Works with I-PAC Ultimate, PacLED64, NanoLED, PacDrive, U-HID, and USB Button
- JSON-based configuration for emulators, ROMs, and button mappings
- Self-managing daemon mode (auto-kills previous instance)
- Integrates with RetroPie's runcommand scripts
- Raspberry Pi (tested on Raspberry Pi 3/4)
- RetroPie
- Ultimarc LED controller (I-PAC Ultimate I/O, PacLED64, NanoLED, PacDrive, U-HID, or USB Button)
- libjson-c library for JSON parsing
- libusb-1.0 for USB communication
- libxml2 (optional, for RGBcommander config converter)
The easiest way to install RetroPac is using the install script:
chmod +x install.sh
./install.shThis will:
- Install all required system dependencies
- Install Node.js (if not present)
- Build retropac, anim-server, and the web interface
- Install to
/usr/local/bin/retropac - Create config at
/etc/retropac/config.json
Install script options:
--no-web- Skip building the web animation editor--no-server- Skip building the anim-server--no-install- Build only, don't install to system
If you prefer to install manually:
sudo apt-get update
sudo apt-get install -y build-essential libjson-c-dev libusb-1.0-0-dev libmicrohttpd-dev
# Optional: for RGBcommander config converter
sudo apt-get install -y libxml2-devmake
make servercd web
npm install
npm run generate
cd ..sudo make installThis will install the binary to /usr/local/bin/retropac and config to /etc/retropac/.
If you have an existing RGBcommander configuration (rgbcmdd.xml), you can convert it to RetroPac format:
make converter
./bin/rgbcmd2retropac rgbcmdd.xml config.jsonSpecify a target controller for the converted ROM configs:
./bin/rgbcmd2retropac rgbcmdd.xml config.json --controller ipac-ultimateIf you have an existing config.json with the old flat ROM format (button colors directly under ROM), you can migrate it to the new per-controller format:
# Migrate using the first controller in your config
./bin/rgbcmd2retropac --migrate config.json
# Migrate and assign ROM configs to a specific controller
./bin/rgbcmd2retropac --migrate config.json --controller pac64This will convert ROM entries from:
"sf2": {
"P1_BUTTON1": "#FF0000"
}To the new per-controller format:
"sf2": {
"controllers": {
"ipac-ultimate": {
"P1_BUTTON1": "#FF0000"
}
}
}The default configuration file is located at /etc/retropac/config.json. You can specify a custom location with the --config option:
retropac --config /path/to/config.json mame sf2.zipExample configuration:
{
"animations_dir": "/home/pi/retropac/animations",
"ipac_controllers": [
{
"device": "ipac-ultimate",
"vendor_id": "0xd209",
"product_id": "0x0410",
"pin_mappings": {
"P1_BUTTON1": { "r_pin": 1, "g_pin": 2, "b_pin": 3 },
"P1_BUTTON2": { "r_pin": 4, "g_pin": 5, "b_pin": 6 }
},
"default": {
"P1_COIN": "#FFFF00",
"P1_START": "#FF0000",
"P1_BUTTON1": "#FFFF00",
"P1_BUTTON2": "#0000FF"
},
"button_labels": {
"P1_BUTTON1": "Punch",
"P1_BUTTON2": "Kick"
}
}
],
"emulators": {
"mame": {
"roms": {
"sf2": {
"controllers": {
"ipac-ultimate": {
"P1_COIN": "#FFFF00",
"P1_START": "#FF0000",
"P1_BUTTON1": "#00FF00",
"P1_BUTTON2": "#00FF00",
"P1_BUTTON3": "#00FF00",
"P1_BUTTON4": "#00FF00",
"P1_BUTTON5": "#00FF00",
"P1_BUTTON6": "#00FF00"
}
}
},
"default": {
"P1_COIN": "#FFFF00",
"P1_START": "#FF0000"
}
}
}
}
}ROM LED configurations are organized by controller, allowing discrete control over which controller receives which colors:
"sf2": {
"controllers": {
"ipac-ultimate": {
"P1_BUTTON1": "#00FF00",
"P1_BUTTON2": "#00FF00"
},
"pac64": {
"P1_COIN": "#FFFF00"
}
}
}This prevents unwanted LED activation when different controllers use the same button names for different purposes (e.g., P1_BUTTON1 on your main controller vs cabinet LEDs on a secondary controller).
Backwards Compatibility: The old flat format is still supported but deprecated:
"sf2": {
"P1_BUTTON1": "#00FF00"
}Legacy configs will broadcast button colors to all controllers. Use the migration tool to convert to the new format.
The optional button_labels section within each controller lets you define friendly names for buttons. These labels are displayed in the web UI for easier identification:
{
"ipac_controllers": [
{
"device": "ipac-ultimate",
"vendor_id": "0xd209",
"product_id": "0x0410",
"pin_mappings": { ... },
"default": { ... },
"button_labels": {
"P1_COIN": "Coin",
"P1_START": "Start",
"P1_BUTTON1": "Light Punch",
"P1_BUTTON2": "Medium Punch",
"P1_BUTTON3": "Heavy Punch",
"P1_BUTTON4": "Light Kick",
"P1_BUTTON5": "Medium Kick",
"P1_BUTTON6": "Heavy Kick"
}
}
]
}Labels appear in:
- Pin mapping cards (below the technical button name)
- Button tooltips in the arcade panel
- Selected button displays
When using multiple controllers, each controller is identified by its device name and can have unique pin mappings:
- Each controller defines which buttons it controls via
pin_mappings - ROM configs specify which controller(s) should receive button colors
- This allows precise control over which LEDs light up on which device
Example with two controllers:
{
"ipac_controllers": [
{
"device": "ipac-ultimate",
"pin_mappings": {
"P1_BUTTON1": { "r_pin": 1, "g_pin": 2, "b_pin": 3 },
"P1_BUTTON2": { "r_pin": 4, "g_pin": 5, "b_pin": 6 }
}
},
{
"device": "pac64",
"pin_mappings": {
"P1_COIN": { "r_pin": 1, "g_pin": 2, "b_pin": 3 },
"P2_COIN": { "r_pin": 4, "g_pin": 5, "b_pin": 6 }
}
}
],
"emulators": {
"mame": {
"roms": {
"sf2": {
"controllers": {
"ipac-ultimate": {
"P1_BUTTON1": "#00FF00",
"P1_BUTTON2": "#0000FF"
},
"pac64": {
"P1_COIN": "#FFFF00"
}
}
}
}
}
}
}In this example, loading Street Fighter 2 will:
- Set
P1_BUTTON1green andP1_BUTTON2blue on theipac-ultimatecontroller - Set
P1_COINyellow on thepac64controller
Note: If you rename a controller's
devicefield in the web UI, all ROM references to that device are automatically updated.
Important: The
animations_dirsetting should use an absolute path when running retropac from RetroPie scripts. Relative paths won't work because the working directory varies depending on how retropac is launched.
To allow non-root users to access the PAC controller, create a udev rule:
sudo nano /etc/udev/rules.d/99-ipac.rulesAdd the following content:
# Ultimarc PAC controllers (Ultimate I/O shown)
SUBSYSTEM=="usb", ATTRS{idVendor}=="d209", MODE="0666"
SUBSYSTEM=="usb_device", ATTRS{idVendor}=="d209", MODE="0666"
Note: Replace
d209with your actual vendor ID if different. Runlsusb | grep -i ultimarcto find your device's vendor ID.
Reload udev rules and unplug/replug the controller:
sudo udevadm control --reload-rules
sudo udevadm triggerAdd to /opt/retropie/configs/all/runcommand-onstart.sh:
#!/bin/bash
# When a game starts, set static LEDs (auto-kills any running animation)
/usr/local/bin/retropac --quiet "$1" "$3"Add to /opt/retropie/configs/all/runcommand-onend.sh:
#!/bin/bash
# When returning to EmulationStation, start attract mode animation
/usr/local/bin/retropac --custom pulse_red --daemon default default defaultMake both scripts executable:
chmod +x /opt/retropie/configs/all/runcommand-onstart.sh
chmod +x /opt/retropie/configs/all/runcommand-onend.shTo start the attract mode animation when EmulationStation first launches (on boot), edit the autostart script:
nano /opt/retropie/configs/all/autostart.shAdd the retropac command before the emulationstation line:
/usr/local/bin/retropac --custom pulse_red --daemon default default default &
emulationstationImportant: The
&at the end runs retropac in the background so EmulationStation continues to start. Without it, EmulationStation would wait for retropac to exit (which it won't in daemon mode).
retropac [options] <emulator> <rom_path> [mode]| Option | Description |
|---|---|
--animate <type> |
Run built-in animation (see Animation Types below) |
--custom <name> |
Run custom animation by filename (without .json) |
--speed <ms> |
Animation speed in milliseconds (default: 50) |
--color <hex> |
Base color for animations (e.g., #FF0000) |
--set-button <name> <color> |
Set a single button LED (e.g., --set-button P1_BUTTON1 #FFFFFF) |
--daemon |
Run as background daemon |
--quiet |
Suppress all console output |
--config <path> |
Custom config file path (default: /etc/retropac/config.json) |
--help |
Show help message |
| Type | Description |
|---|---|
rainbow |
Rotating rainbow colors across all buttons |
breathing |
Smooth fade in/out pulse effect |
chase |
Running light with trailing fade |
sparkle |
Random sparkle effect |
color_cycle |
Cycle through a list of colors |
Run with a specific game (sets LEDs based on emulator/ROM config):
retropac mame /home/pi/RetroPie/roms/mame/sf2.zipUse default button configuration (EmulationStation menu):
retropac default default defaultRun rainbow animation as daemon (background):
retropac --animate rainbow --daemon default default defaultRun breathing animation with red color at 30ms speed:
retropac --animate breathing --color '#FF0000' --speed 30 default default defaultRun chase animation in foreground (Ctrl+C to stop):
retropac --animate chase --color '#00FF00' default default defaultRun sparkle animation with blue:
retropac --animate sparkle --color '#0000FF' --daemon default default defaultSet a single button LED to white (useful for testing pin mappings):
retropac --set-button P1_BUTTON1 '#FFFFFF'Turn off a single button LED:
retropac --set-button P1_BUTTON1 '#000000'Set button to red in quiet mode (no output):
retropac --quiet --set-button P1_START '#FF0000'Note: The
--set-buttonoption doesn't require emulator/ROM arguments. It directly controls a single LED using the pin mappings from config.json.
Run a custom animation by filename:
retropac --custom rainbow_wave default default defaultRun custom animation as daemon:
retropac --custom rainbow_wave --daemon default default defaultRun the idle animation configured in config.json:
retropac --custom idle --daemon default default defaultWhen a game starts (in runcommand-onstart.sh):
#!/bin/bash
# Set static LEDs for the game (auto-kills any running animation)
/usr/local/bin/retropac "$1" "$3"When a game ends (in runcommand-onend.sh):
#!/bin/bash
# Start attract mode with custom animation
/usr/local/bin/retropac --custom idle --daemon default default default
# Or use a built-in animation
# /usr/local/bin/retropac --animate rainbow --daemon default default defaultRetroPac automatically manages its daemon process:
- Each invocation kills any existing retropac daemon
- PID is stored in
/tmp/retropac.pid - No need for manual
pkillin shell scripts
- P1_COIN, P2_COIN, P3_COIN, P4_COIN
- P1_START, P2_START, P3_START, P4_START
- P1_BUTTON1-6, P2_BUTTON1-6, P3_BUTTON1-6, P4_BUTTON1-6
- P1_JOYSTICK, P2_JOYSTICK, P3_JOYSTICK, P4_JOYSTICK
- P1_TRACKBALL, P2_TRACKBALL, P3_TRACKBALL, P4_TRACKBALL
RetroPac supports custom animations defined in JSON files. See docs/ANIMATIONS.md for the complete format specification.
- Create an
animationsdirectory alongside your config.json - Add animation JSON files (e.g.,
rainbow_wave.json) - Configure the idle animation in config.json:
{
"animations_dir": "animations",
"idle_animation": "rainbow_wave",
"ipac_controllers": [...],
"default": {...},
"emulators": {...}
}{
"name": "Rainbow Wave",
"speed": 50,
"loop": true,
"frames": [
{
"buttons": [
{"button": "P1_BUTTON1", "color": "#FF0000"},
{"button": "P1_BUTTON2", "color": "#FF7F00"}
],
"fade": true,
"fade_speed_ms": 200
}
]
}| Field | Description |
|---|---|
name |
Display name for the animation |
speed |
Base timing interval in milliseconds |
loop |
Whether animation repeats (true/false) |
frames |
Array of frame objects |
| Field | Description |
|---|---|
buttons |
Array of button-color pairs to set in this frame |
fade |
Whether to fade to the colors |
fade_speed_ms |
Fade duration in milliseconds |
| Field | Description |
|---|---|
button |
Button identifier (e.g., "P1_BUTTON1") |
color |
Target color in hex format |
controller |
(Optional) Controller index (0-based) to target. Omit to apply to all controllers. |
retropac/
├── src/ # Source files
│ ├── main.c # Main program entry
│ ├── config.c # JSON configuration parsing
│ ├── ipac.c # PAC USB communication
│ └── animation.c # LED animation engine
├── include/ # Header files
│ └── retropac.h
├── animations/ # Custom animation files
│ ├── rainbow_wave.json
│ ├── pulse_red.json
│ └── startup_sequence.json
├── web/ # Animation Editor web app
│ ├── app.vue # Main Vue component
│ ├── components/ # Vue components
│ └── composables/ # API composables
├── docs/ # Documentation
│ └── ANIMATIONS.md # Custom animation format docs
├── tools/ # Utility tools
│ ├── rgbcmd2retropac.c # RGBcommander converter
│ └── anim-server.c # Animation Editor HTTP server
├── config.example.json # Example configuration
├── Makefile
└── README.md
RetroPac includes a visual web-based animation editor for creating and editing custom animation files.
For complete setup instructions, see docs/EDITOR_SETUP.md.
# Install dependencies
sudo apt install libmicrohttpd-dev libjson-c-dev
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install nodejs
# Build server and web app
make server
cd web && npm install && npm run generate && cd ..
# Run the server
./bin/anim-serverThen open a browser on another computer and navigate to:
http://<your-raspberry-pi-ip>:8080
- Visual arcade panel layout - Click buttons to select them
- Color picker - Choose colors for selected buttons
- Frame timeline - Add, remove, and reorder animation frames
- Drag-and-drop reordering - Rearrange frames by dragging them in the timeline
- Live preview - Play animations with real-time frame highlighting
- Frame settings - Configure fade, fade speed, and delay per frame
- Animation settings - Set name, speed, and loop options
- Save/Load - Animations are saved directly to the animations directory
- Full-width responsive layout - Works on any screen size
The web application also includes a Config Editor for managing your RetroPac configuration file (config.json) directly from the browser.
Navigate to the Config tab in the web interface, or go directly to:
http://<your-raspberry-pi-ip>:8080/config
- Add/Remove Controllers - Manage multiple PAC controllers
- Editable Device Settings - Modify device name, vendor ID, and product ID
- Pin Mappings - Configure RGB pin numbers for each button
- Test Button - Click the 💡 icon to light up a button on the hardware (verifies correct pin mapping)
- Friendly Labels - Custom button labels are displayed below the technical name
- Add Button Dropdown - Select from available button names (filters out already-used buttons)
- Custom Names - Define friendly names for buttons (e.g., "Punch", "Kick", "Jump")
- Display Everywhere - Labels appear in pin mappings, tooltips, and selected button displays
- Per-Button Configuration - Set labels for any or all of the 86 supported buttons
- Visual Color Picker - Set default LED colors for each button
- Add/Remove Buttons - Configure which buttons have default colors
- Button Name on Top - Clear card layout with color picker below
- Emulator Management - Add, remove, and configure emulators
- ROM Configuration - Set per-ROM button colors
- Duplicate ROM - Quickly copy a ROM configuration as a starting point
- Alphabetical Sorting - Emulators and ROMs are sorted for easy navigation
- Expandable Sections - Collapse/expand ROMs to manage large configurations
- Auto-Save Detection - Unsaved changes are tracked with a visual indicator
- Backup Function - Create timestamped backups (
config-bak-yyyyMMddHHmmss.json) - Toast Notifications - Feedback for save, backup, and error states
- Responsive Design - Works on desktop and tablet screens
The server provides the following REST API endpoints for configuration:
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/config |
Get the full configuration |
PUT |
/api/config |
Save the full configuration |
POST |
/api/config/backup |
Create a backup of config.json |
GET |
/api/buttons |
Get list of valid button names |
GET |
/api/version |
Get server version info |
MIT License

