Twitch LED Matrix - Part 3: RGB Lights and .Fill()

Heads Up: As I share my IRL adventures and tech builds here on the CabinetTerror blog, you might spot some affiliate links. If you click on those links to buy streaming gear or grab parts for your own LED screens, I may earn a small commission at absolutely no extra cost to you. It helps fund future projects, and I only recommend stuff I actually use and love. Thanks for the support!

Welcome to Part 3 of the Twitch LED Matrix build! In Part 1 we built a live text scroller, and in Part 2 we learned how to drop in animated GIFs like Pac-Man.

Today, we are skipping external files entirely. We are going to use the matrix's raw power to create full-screen environmental effects—specifically, a set of aggressive, flashing Police Lights that trigger directly from your Twitch chat. This is the perfect alert for hype trains, boss fights, or channel point redemptions!

1. The Magic of RGB and .Fill()

Because we are just telling the LEDs to turn on and off in solid colors, we don't need to transfer any files via WinSCP this time. The Python graphics library has a built-in command called .Fill(R, G, B).

It uses standard RGB (Red, Green, Blue) color codes, where values range from 0 (off) to 255 (maximum brightness). For example, .Fill(255, 0, 0) turns the entire board pure Red, while .Fill(0, 0, 255) turns it pure Blue.

2. Updating the Python Code

Let's add the police flasher logic into our main script. This code builds on our previous work, meaning your text ticker and your Pac-Man animation will still work perfectly!

  1. Open your terminal/PowerShell and SSH into your Pi: ssh twitchmatrix@twitchmatrix.local
  2. Open your script: nano twitch_scroller.py
  3. Replace your old code with this updated version (Notice the new POLICE FLASHER section inside the while True: loop!):
#!/usr/bin/env python3
import time
import math
from PIL import Image, ImageSequence
from rgbmatrix import RGBMatrix, RGBMatrixOptions, graphics
import paho.mqtt.client as mqtt

# --- CONFIGURATION ---
MQTT_BROKER = "localhost"  
MQTT_TOPIC = "twitch/scroll"
LED_ROWS = 32      
LED_COLS = 64      
LED_CHAIN = 2  
GPIO_MAPPING = 'adafruit-hat'  

DEFAULT_MSG = "Waiting for Chat..." 
TEXT_HEIGHT = 22 
GREEN = graphics.Color(0, 255, 0)

current_message = DEFAULT_MSG
new_message_arrived = False
current_color = GREEN

def on_connect(client, userdata, flags, rc):
    client.subscribe(MQTT_TOPIC)

def on_message(client, userdata, msg):
    global current_message, new_message_arrived
    try:
        current_message = msg.payload.decode("utf-8").strip()
        new_message_arrived = True 
    except:
        pass

def run_matrix():
    global current_message, new_message_arrived, current_color
    options = RGBMatrixOptions()
    options.rows = LED_ROWS
    options.cols = LED_COLS
    options.chain_length = LED_CHAIN
    options.hardware_mapping = GPIO_MAPPING
    options.drop_privileges = False
    options.brightness = 40  
    
    matrix = RGBMatrix(options = options)
    offscreen_canvas = matrix.CreateFrameCanvas()

    # --- LOAD THE GIF (From Part 2) ---
    pacman_frames = []
    try:
        pacman_gif = Image.open("/home/twitchmatrix/pacman.gif")
        for frame in ImageSequence.Iterator(pacman_gif):
            frame = frame.convert('RGB')
            frame = frame.resize((offscreen_canvas.width, 32), Image.NEAREST)
            pacman_frames.append(frame)
    except Exception as e:
        print("Could not load pacman gif:", e)
    
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    
    try:
        client.connect(MQTT_BROKER, 1883, 60)
        client.loop_start() 
    except:
        print("MQTT Connection Failed")

    pos = offscreen_canvas.width
    is_scrolling_custom = False
    hue = 0 

    while True:
        if new_message_arrived:
            
            # --- PAC-MAN ANIMATION ---
            if current_message == "!pacman" and pacman_frames:
                end_time = time.time() + 5  
                while time.time() < end_time:
                    for frame in pacman_frames:
                        offscreen_canvas.Clear()
                        x_offset = (offscreen_canvas.width - frame.width) // 2
                        y_offset = (offscreen_canvas.height - frame.height) // 2
                        offscreen_canvas.SetImage(frame, x_offset, y_offset)
                        offscreen_canvas = matrix.SwapOnVSync(offscreen_canvas)
                        time.sleep(0.1) 
                
                current_message = DEFAULT_MSG
                new_message_arrived = False
                pos = offscreen_canvas.width
                is_scrolling_custom = False
                continue

            # --- FULL SCREEN POLICE FLASHER ---
            elif current_message == "!police":
                end_time = time.time() + 5  # Total time the effect runs
                
                while time.time() < end_time:
                    # Paint board RED
                    offscreen_canvas.Fill(255, 0, 0)
                    offscreen_canvas = matrix.SwapOnVSync(offscreen_canvas)
                    time.sleep(0.15) # Strobe speed
                    
                    # Paint board BLUE
                    offscreen_canvas.Fill(0, 0, 255)
                    offscreen_canvas = matrix.SwapOnVSync(offscreen_canvas)
                    time.sleep(0.15) # Strobe speed
                
                # Reset back to default text when done
                current_message = DEFAULT_MSG
                new_message_arrived = False
                pos = offscreen_canvas.width
                is_scrolling_custom = False
                continue

            # --- NORMAL TEXT SCROLLING ---
            elif current_message != DEFAULT_MSG:
                pos = offscreen_canvas.width
                is_scrolling_custom = True 
                new_message_arrived = False 

        offscreen_canvas.Clear()
        
        if is_scrolling_custom:
            r = int(127 * math.sin(hue) + 128)
            g = int(127 * math.sin(hue + 2) + 128)
            b = int(127 * math.sin(hue + 4) + 128)
            current_color = graphics.Color(r, g, b)
            hue += 0.1 
        else:
            current_color = GREEN

        length = graphics.DrawText(offscreen_canvas, font, pos, TEXT_HEIGHT, current_color, current_message)
        pos -= 1

        if (pos + length < 0):
            if is_scrolling_custom:
                current_message = DEFAULT_MSG
                is_scrolling_custom = False
            pos = offscreen_canvas.width

        time.sleep(0.03) 
        offscreen_canvas = matrix.SwapOnVSync(offscreen_canvas)

if __name__ == "__main__":
    font = graphics.Font()
    font.LoadFont("/home/twitchmatrix/rpi-rgb-led-matrix/fonts/7x13.bdf")
    run_matrix()

Don't forget to change the 2nd to last line of code to your own Username if you did not set it as twitchmatrix.

Save it by pressing CTRL+X, then Y, then ENTER.

Restart the matrix service so the changes take effect:
sudo systemctl restart twitch_led.service

3. Customizing Your Effects

You don't have to stick with police lights! You can modify three simple variables inside the !police code block to create entirely new alerts:

  • Duration: Change end_time = time.time() + 5 to any number. If you want the alert to last for 10 seconds, just change the 5 to a 10.
  • Colors: Look for the .Fill(255, 0, 0) lines. You can use any RGB color code! Want a "Red Alert" strobe instead? Change the blue .Fill(0, 0, 255) to pure black .Fill(0, 0, 0) so it alternates between Red and Off.
  • Strobe Speed: Look for the time.sleep(0.15) lines. A lower number (like 0.05) will make it flash insanely fast. A higher number (like 0.5) will create a slow, rhythmic pulse. Make sure to keep both sleep numbers the same so the rhythm stays even!

4. Linking to Lumia Stream

Just like we did in Part 2, we need to create a chat command in Lumia Stream to trigger the script.

  1. Open Lumia Stream on your PC.
  2. Go to Commands > Chat Commands > Add Command.
  3. Name the command police.
  4. Go to the Lumia Actions tab, click Add Action, and choose MQTT.
  5. Fill out the box exactly like this:
    • Topic: twitch/scroll
    • Value: !police
  6. Click Save, then click the Refresh button on your Lumia Dashboard to lock it in.

Hit the Lights!

Go to your Twitch chat and type !police. The entire room should immediately reflect the red and blue glow of your LED boards!

Comments