A graphical LED color controller for TrimUI Brick, Brick Hammer, and Smart Pro running NextUI.
- Visual device view — Front and back representations of your device with LED zones in their physical positions
- 2D HSL color picker — Full-screen color field with crosshair cursor and live hardware preview
- Per-zone settings — Brightness, effect type, and animation speed for each LED zone
- Real-time feedback — Physical LEDs update instantly as you pick colors and adjust settings
- LEDs Off/On toggle — Turn all LEDs off with a single button press; toggle back on to resume your previous state (static colors or running animation)
- Per-LED animations — 18 animated LED patterns using per-LED frame control (11 Brick, 7 Smart Pro)
- Persistent animations — Animations survive app exit and device reboots
- Multi-device support — Automatically detects Brick, Brick Hammer, or Smart Pro and adapts the UI
- Random front images — Device view randomly picks from available front image variants on each view flip (app start for Smart Pro)
- NextUI compatible — Reads and writes the same settings file as the built-in LED control
| Zone | View | Description |
|---|---|---|
| FN1 Key | Front | Left function key LED |
| FN2 Key | Front | Right function key LED |
| Top Bar | Back | LED strip along the top edge |
| L/R Triggers | Back | Left and right trigger LEDs |
Note: FN1 and FN2 share a brightness path — changing one changes the other.
| Zone | View | Description |
|---|---|---|
| Joystick L | Front | Left joystick ring LED |
| Logo | Front | Triangle logo indicator LED |
| Joystick R | Front | Right joystick ring LED |
Note: All Smart Pro zones share a single brightness path — changing brightness on any zone changes all of them.
![]() |
![]() |
![]() |
| Brick — Front | Brick — Back | Smart Pro |
![]() |
![]() |
![]() |
| Color Picker | Zone Settings | Menu |
| Button | Action |
|---|---|
| D-pad L/R | Select zone (front view) |
| D-pad U/D | Select zone (back view, Brick only) |
| L1/R1 | Switch front/back view (Brick only) |
| A | Open color picker for selected zone |
| Y | Menu (zone settings, animations, about) |
| Menu | Toggle LEDs off/on |
| B | Save and quit |
| Button | Action |
|---|---|
| D-pad (8-direction) | Move cursor to pick color |
| L1/R1 + D-pad | Fast cursor movement |
| A | Confirm color |
| B | Cancel (restore original color) |
| Button | Action |
|---|---|
| D-pad U/D | Move between settings rows |
| D-pad L/R | Adjust value |
| L1/R1 + L/R | Fast adjust |
| A | Confirm |
| B | Cancel (restore original values) |
Per-LED animation patterns that drive individually addressable LEDs via the frame_hex framebuffer. The app automatically discovers and shows only the animations that match your device. Animations run as background daemons that persist after the app exits and automatically restart on reboot.
Select animations from Y → Menu → Animations. While an animation is running, editing zone colors will pause the animation and resume it when you're done.
Animations are discovered dynamically from the scripts/ folder inside the pak directory. Each script declares its target platform via a # PLATFORM: header. You can add your own — see Creating Custom Animations below.
| Animation | Description |
|---|---|
| Aurora | Slow-drifting greens, teals, and purples |
| Binary | Binary counter in LEDs |
| Campfire | Flickering warm firelight |
| Comet | Bright head with fading tail |
| EKG | Heartbeat pulse sweep |
| K.I.T.T. | Classic Knight Rider red sweep |
| Kessel Run | Stars streak outward from center to lightspeed |
| Morse | Blinks "NEXTUI" in Morse code |
| Ocean | Rolling blue-green waves |
| Police | Red and blue alternating flash |
| Pride | Rainbow flag color chase |
| Animation | Description |
|---|---|
| Breathe | All LEDs pulse warm orange together |
| Fill | Rings fill up from bottom, then drain back down |
| Helix | Two colored dots chase each other around each ring |
| Pulse | Alternating ring pulses — left brightens, then right |
| Radar | Bright head with fading tail sweeping each ring |
| Rainbow | 8-color rainbow slowly rotating around rings |
| Spin | Counter-rotating dots on left and right rings |
Drop any .sh file into the scripts/ folder inside the LED'oh! pak directory and it will automatically appear in the Animations menu. No recompilation needed.
Location: Tools/tg5040/LED'oh!.pak/scripts/
Requirements:
- The script must be a POSIX shell script (starts with
#!/bin/sh) - Add a
# NAME: Your Animation Nameline in the first 10 lines to set the display name (otherwise the filename is used) - Add a
# PLATFORM: brickor# PLATFORM: smartproline to target the correct device (defaults tobrickif omitted) - The script must write all LED positions in every frame to avoid stale colors bleeding through
- Use PID file management so the app can detect and stop your animation
Brick LED positions in frame_hex (14 LEDs, space-separated hex colors):
Position: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Zone: |---------- Top Bar (8) ----------| F2 F1 |-- L Trig --|-- R Trig --|
Smart Pro LED positions in frame_hex (23 LEDs, space-separated hex colors):
Position: 1 2-12 13-23
Zone: Logo |-- Left Ring (11) --|-- Right Ring (11) --|
Note: The Smart Pro logo LED uses GRB byte order instead of RGB.
All positions must be written in every frame, even if unused (set unused positions to 000000).
Template:
#!/bin/sh
# NAME: My Animation
# PLATFORM: brick
# Description of what it does
#
# Daemon-ready: writes PID to /tmp/led_anim.pid for external management
F="/sys/class/led_anim/frame_hex"
E="/sys/class/led_anim/effect_enable"
PIDFILE="/tmp/led_anim.pid"
SPEED=0.10 # seconds between frames
# --- PID management (required) ---
if [ -f "$PIDFILE" ]; then
OLD_PID=$(cat "$PIDFILE")
if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then
kill "$OLD_PID" 2>/dev/null
sleep 0.5
fi
rm -f "$PIDFILE"
fi
echo $$ > "$PIDFILE"
# --- Setup (required) ---
# Set brightness for each zone (0-100)
echo 80 > /sys/class/led_anim/max_scale # Top bar
echo 50 > /sys/class/led_anim/max_scale_f1f2 # FN1/FN2 buttons
echo 50 > /sys/class/led_anim/max_scale_lr # L/R triggers
# Clear the effect system so frame_hex has full control
echo "000000 " > /sys/class/led_anim/effect_rgb_hex_m
echo "000000 " > /sys/class/led_anim/effect_rgb_hex_f1
echo "000000 " > /sys/class/led_anim/effect_rgb_hex_f2
echo "000000 " > /sys/class/led_anim/effect_rgb_hex_lr
echo 0 > /sys/class/led_anim/effect_m
echo 0 > /sys/class/led_anim/effect_f1
echo 0 > /sys/class/led_anim/effect_f2
echo 0 > /sys/class/led_anim/effect_lr
echo 0 > "$E"
sleep 0.3
# --- Cleanup on exit (required) ---
cleanup() {
rm -f "$PIDFILE"
echo 1 > "$E"
echo 4 > /sys/class/led_anim/effect_m
echo -1 > /sys/class/led_anim/effect_cycles_m
echo 50 > /sys/class/led_anim/max_scale
echo 50 > /sys/class/led_anim/max_scale_f1f2
echo 50 > /sys/class/led_anim/max_scale_lr
exit 0
}
trap cleanup INT TERM HUP
# --- Write one frame (required) ---
write_frame() {
echo "$1 " > "$F"
sleep $SPEED
echo 1 > "$E"
echo 0 > "$E"
}
# --- Your animation loop ---
while true; do
# Build your frame as 14 space-separated 6-digit hex colors (RRGGBB)
# Top bar (8) + F2 + F1 + TL + TL2 + TR2 + TR
write_frame "FF0000 00FF00 0000FF FF0000 00FF00 0000FF FF0000 00FF00 000000 000000 000000 000000 000000 000000"
doneTips:
- Colors are 6-digit hex (RRGGBB):
FF0000= red,00FF00= green,000000= off SPEEDcontrols frame timing — lower is faster (0.08 = fast, 0.20 = slow)- Use shell arithmetic for patterns:
$(( step % 8 ))for cycling - The
write_framefunction handles the effect_enable toggle that flushes frame data to hardware - Set
max_scaleto 0 for zones you want completely off (overrides any color)
LED'oh! is available in the NextUI Pak Store. Install it directly from the store on your device.
- Download the latest release zip from Releases
- Extract to your SD card — the
LED'oh!.pakfolder goes inTools/tg5040/ - Launch from the Tools menu in NextUI
- TrimUI Brick
- TrimUI Brick Hammer
- TrimUI Smart Pro
Requires Docker and the NextUI tg5040 toolchain image.
make build # Build via Docker
make package # Build + create distribution zip
make clean # Remove build artifactsThe binary is cross-compiled using the ghcr.io/loveretro/tg5040-toolchain Docker image. No local cross-compiler setup required.
See trimui_brick_led_research.md for per-LED framebuffer findings on the TrimUI Brick (14 individually addressable LEDs via frame_hex). The Smart Pro uses the same frame_hex interface with 23 LEDs: 1 logo + 11 left ring + 11 right ring.
- Built with PakKit and Apostrophe
- For NextUI
MIT — see LICENSE






