Breathing Life into the AKAI APC Key 25 in FL Studio: A Python Scripting Guide

If you’ve ever plugged an AKAI APC Key 25 (specifically the MK1) into FL Studio, you might have felt a bit underwhelmed. Out of the box, it behaves like a generic MIDI keyboard. Those beautiful translucent pads sit lifeless, and the transport controls don’t quite sync up the way you want.

But here is the good news: FL Studio’s Python MIDI Scripting API lets us take full control of this hardware. In this post, we’re going to build a custom script from scratch. We’ll make it genuinely useful by mapping transport controls and FPC drum pads, and then we’ll add some serious flair: a chaotic, random-flashing “Lite-Brite” sequence that activates whenever the DAW is stopped.

Let’s dive in!

Step 1: The Setup and File Format

FL Studio looks for custom scripts in a specific directory.

  1. Navigate to your user data folder:
    • Windows: Documents\Image-Line\FL Studio\Settings\Hardware\
    • macOS: Documents/Image-Line/FL Studio/Settings/Hardware/
  2. Create a new folder here and call it something like AKAI APCKey25 Custom.
  3. Inside that folder, create a plain text file named device_APCKey25.py. Open it in your favorite code editor.

The Script Header & Imports
Every FL Studio Python script needs a specific header at the very top so the DAW knows what to call it in the menus. Below that, we must import the internal modules we plan to use.

Start your blank .py file by adding this at the very top:

# name=AKAI APCKey25 Custom
# author=Your Name

import device
import midi
import transport
import time
import random

Step 2: Using the Debugger

Before we write the logic, you need to know how to test your code. Since we are programming hardware, the Script Output window is your best friend.

  1. Open FL Studio and go to View > Script output.
  2. This window will show any Python errors (Tracebacks) if you make a typo.
  3. It also displays anything you push through the print() function in your code. This is incredibly useful for finding out what MIDI IDs your buttons are sending!
  4. Whenever you save changes to your .py file, click the Reload button at the bottom of this window to instantly apply and restart your script.

Step 3: The “Secret Handshake”

Here is a secret that drives many scripters crazy: The APC Key 25 MK1 powers on in a “Generic” mode where it completely ignores LED feedback commands. To fix this, we have to send a specific SysEx (System Exclusive) message to unlock it.

Add this function right below your import statements. FL Studio automatically runs OnInit() the moment your script is loaded or reloaded:

def OnInit():
    print("APCKey25 Initialized! Unlocking LEDs...")
    # The 'Secret Handshake' to unlock Ableton/Alternate mode
    sysex_msg = bytes([0xF0, 0x47, 0x7F, 0x27, 0x60, 0x00, 0x04, 0x41, 0x08, 0x02, 0x01, 0xF7])
    device.midiOutSysex(sysex_msg)

Step 4: Making it Useful (Transport & Pads)

Now let’s map the Play/Pause button (MIDI ID 91) to control FL Studio’s transport, and map the bottom row of our grid (IDs 0-7) to the standard FPC drum machine layout (starting at MIDI Note 36).

Add this block of code below your OnInit function:

# Map physical pads 0-7 to FPC notes 36-43
pad_map = {0: 36, 1: 37, 2: 38, 3: 39, 4: 40, 5: 41, 6: 42, 7: 43}

def OnMidiMsg(event):
    event.handled = False

    # Transport: Play/Pause Button (ID 91)
    if event.status == midi.MIDI_NOTEON and event.data1 == 91 and event.data2 > 0:
        transport.start()
        event.handled = True
        return

    # Pad Remapping for FPC
    if event.status in [midi.MIDI_NOTEON, midi.MIDI_NOTEOFF]:
        if event.data1 in pad_map:
            event.data1 = pad_map[event.data1]
            event.handled = False # Let the modified note through to the DAW

Step 5: The Fun Part (Idle Chaos Mode)

Now for the grand finale. We want our 40-pad grid to go crazy with random colours (Green, Red, and Amber) whenever the track is stopped, but immediately clear out when we hit play.

Add this below your OnMidiMsg function. The OnIdle() function runs constantly in the background (around 30 frames per second), making it perfect for our animation loop.

# Define the 40 main pads on the APC Key 25
GRID_PADS = range(40)

def OnIdle():
    if transport.isPlaying():
        # If the DAW is playing, turn off the noise
        for note_id in GRID_PADS:
            device.midiOutMsg(0x90 | (note_id << 8) | (0 << 16))
    else:
        # If stopped, run the random flashing sequence!
        # Colors: 0=Off, 1=Green, 3=Red, 5=Amber
        colors = [0, 1, 3, 5] 

        # Update 15 random pads per frame so it looks like flickering noise
        for _ in range(15): 
            random_pad = random.choice(GRID_PADS)
            random_color = random.choice(colors)

            # Send the MIDI message to light up the LED
            device.midiOutMsg(0x90 | (random_pad << 8) | (random_color << 16))

Step 6: Cleaning Up

Finally, it’s good practice to turn off all the lights when you close FL Studio so your controller doesn’t stay lit up forever. Add this at the very bottom of your script:

def OnDeInit():
    # Turn everything off when the script closes
    for note_id in range(127):
        device.midiOutMsg(0x90 | (note_id << 8) | (0 << 16))

The Complete Script

If you want to jump right into the action, copy and paste this complete code into your device_APCKey25.py file:

# name=AKAI APCKey25 Custom
# author=Your Name

import device
import midi
import transport
import time
import random

def OnInit():
    print("APCKey25 Initialized! Unlocking LEDs...")
    sysex_msg = bytes([0xF0, 0x47, 0x7F, 0x27, 0x60, 0x00, 0x04, 0x41, 0x08, 0x02, 0x01, 0xF7])
    device.midiOutSysex(sysex_msg)

pad_map = {0: 36, 1: 37, 2: 38, 3: 39, 4: 40, 5: 41, 6: 42, 7: 43}

def OnMidiMsg(event):
    event.handled = False

    if event.status == midi.MIDI_NOTEON and event.data1 == 91 and event.data2 > 0:
        transport.start()
        event.handled = True
        return

    if event.status in [midi.MIDI_NOTEON, midi.MIDI_NOTEOFF]:
        if event.data1 in pad_map:
            event.data1 = pad_map[event.data1]
            event.handled = False 

GRID_PADS = range(40)

def OnIdle():
    if transport.isPlaying():
        for note_id in GRID_PADS:
            device.midiOutMsg(0x90 | (note_id << 8) | (0 << 16))
    else:
        colors = [0, 1, 3, 5] 
        for _ in range(15): 
            random_pad = random.choice(GRID_PADS)
            random_color = random.choice(colors)
            device.midiOutMsg(0x90 | (random_pad << 8) | (random_color << 16))

def OnDeInit():
    for note_id in range(127):
        device.midiOutMsg(0x90 | (note_id << 8) | (0 << 16))

Wrapping Up & Testing

To test your creation:

  1. Save your Python file.
  2. Open FL Studio, press F10 for MIDI Settings.
  3. Crucial step: Make sure your Input Port and Output Port for the APC Key 25 are set to the exact same number so the LEDs can receive data.
  4. Select your custom script (AKAI APCKey25 Custom) from the “Controller type” dropdown.
  5. Go to View > Script output and hit Reload.

Hit stop, and watch your grid come alive with chaotic, funky colours. Hit play, and the lights clear out, letting you get back to making music.

Happy scripting!