Eight-Step Drummer

This sequencer is perfect for getting started! It's a lot of fun to play, and very intuitive, since all four sample tracks are displayed continuously. No "menu-diving" allowed!

You'll program it with CircuitPython, so first, let's prep the board for that.

CircuitPython Prep

To prep the Trellis M4 to run the sequencer code, follow these steps:

If you'd like to reduce the storage footprint of the lib folder (to make room for lots of sound files!) these are the only one's you'll need to keep, you can remove the rest:

  • adafruit_trellism4.mpy
  • adafruit_fancyled folder
  • neopixel.mpy
  • adafruit_matrixkeypad.mpy
  • adafruit_adxl34x.mpy
  • adafruit_bus_device

Prepare Audio Files

Audio files can be gathered by any means you like, but they will all need to be formatted the same way to be recognized by the Trellis M4.

See this guide on how to convert audio files

For the Eight-Step Simple Sequencer you can use either mono or stereo files, just make sure all of your files are the same format, no mixing and matching!

In order to prevent the program from playing errant .wav files that may already be on the drive, delete any .wav files you may see left over from other projects on your CIRCUITPY drive before adding new ones.

Sound Packs

For your convenience and drumming pleasure, we've compiled some sound kits for you to use. Simply download this zip file, uncompress it, and you'll be able to use them on the board after copying and renaming them as outlined below.

Samples Directory

With your Trellis M4 plugged into your computer, it will show up as the CIRCUITPY USB drive. Open it up and create a new folder named samples

Copy Sounds

Copy a set of four sound files from one of the packs and paste it into the samples folder you just made on the Trellis M4's CIRCUITPY drive.


Rename the four files like this:

  • drum_cymbal_pedal.wav to voice01.wav
  • drum_snare_hard.wav to voice02.wav
  • elec_ping.wav to voice03.wav
  • bd_haus.wav to voice04.wav

As you'll see in the code below, this name pattern: /samples/voice01.wav through /samples/voice04.wavis the only set of names that will be found and played.

When we code the Trellis M4 with the sequencer program, the voices (sound files) will be arrayed along the four rows as shown here.

CircuitPython Code

Everything is now prepared, you just need the actual code!

Copy the code shown here, and then paste it into Mu. Save it to your Trellis M4 as code.py

import time
import board
import busio
import audioio
import adafruit_fancyled.adafruit_fancyled as fancy
import adafruit_trellism4
import adafruit_adxl34x

tempo = 180  # Starting BPM

# You can use the accelerometer to speed/slow down tempo by tilting!

SAMPLE_FOLDER = "/samples/"  # the name of the folder containing the samples
# You get 4 voices, they must all have the same sample rate and must
# all be mono or stereo (no mix-n-match!)
VOICES = [SAMPLE_FOLDER+"voice01.wav",

# four colors for the 4 voices, using 0 or 255 only will reduce buzz
DRUM_COLOR = ((0, 255, 255),
              (0, 255, 0),
              (255, 255, 0),
              (255, 0, 0))
# For the intro, pick any number of colors to make a fancy gradient!
INTRO_SWIRL = [fancy.CRGB(255, 0, 0),  # red
               fancy.CRGB(0, 255, 0),  # green
               fancy.CRGB(0, 0, 255)]  # blue
# the color for the sweeping ticker bar
TICKER_COLOR = (255, 255, 255)

# Our keypad + neopixel driver
trellis = adafruit_trellism4.TrellisM4Express(rotation=90)

# Our accelerometer
accelerometer = adafruit_adxl34x.ADXL345(i2c)

def wheel(pos): # Input a value 0 to 255 to get a color value.
    if pos < 0 or pos > 255:
        return (0, 0, 0)
    elif pos < 85:
        return(int(pos * 3), int(255 - pos*3), 0)
    elif pos < 170:
        pos -= 85
        return(int(255 - pos*3), 0, int(pos * 3))
        pos -= 170
        return(0, int(pos * 3), int(255 - pos*3))

# Play the welcome wav (if its there)
with audioio.AudioOut(board.A1, right_channel=board.A0) as audio:
        f = open("welcome.wav", "rb")
        wave = audioio.WaveFile(f)
        swirl = 0  # we'll swirl through the colors in the gradient
        while audio.playing:
            for i in range(32):
                palette_index = ((swirl+i) % 32) / 32
                color = fancy.palette_lookup(INTRO_SWIRL, palette_index)
                # display it!
                trellis.pixels[(i//8, i%8)] = color.pack()
            swirl += 1
        # Clear all pixels
        # just hold a moment
    except OSError:
        # no biggie, they probably deleted it

# Parse the first file to figure out what format its in
with open(VOICES[0], "rb") as f:
    wav = audioio.WaveFile(f)
    print("%d channels, %d bits per sample, %d Hz sample rate " %
          (wav.channel_count, wav.bits_per_sample, wav.sample_rate))

    # Audio playback object - we'll go with either mono or stereo depending on
    # what we see in the first file
    if wav.channel_count == 1:
        audio = audioio.AudioOut(board.A1)
    elif wav.channel_count == 2:
        audio = audioio.AudioOut(board.A1, right_channel=board.A0)
        raise RuntimeError("Must be mono or stereo waves!")
    mixer = audioio.Mixer(voice_count=4,

samples = []
# Read the 4 wave files, convert to stereo samples, and store
# (show load status on neopixels and play audio once loaded too!)
for v in range(4):
    trellis.pixels[(v, 0)] = DRUM_COLOR[v]
    wave_file = open(VOICES[v], "rb")
    # OK we managed to open the wave OK
    for x in range(1, 4):
        trellis.pixels[(v, x)] = DRUM_COLOR[v]
    sample = audioio.WaveFile(wave_file)
    # debug play back on load!
    mixer.play(sample, voice=0)
    for x in range(4, 7):
        trellis.pixels[(v, x)] = DRUM_COLOR[v]
    while mixer.playing:
    trellis.pixels[(v, 7)] = DRUM_COLOR[v]

# Clear all pixels

# Our global state
current_step = 7 # we actually start on the last step since we increment first
# the state of the sequencer
beatset = [[False] * 8, [False] * 8, [False] * 8, [False] * 8]
# currently pressed buttons
current_press = set()

while True:
    stamp = time.monotonic()
    # redraw the last step to remove the ticker bar (e.g. 'normal' view)
    for y in range(4):
        color = 0
        if beatset[y][current_step]:
            color = DRUM_COLOR[y]
        trellis.pixels[(y, current_step)] = color

    # next beat!
    current_step = (current_step + 1) % 8

    # draw the vertical ticker bar, with selected voices highlighted
    for y in range(4):
        if beatset[y][current_step]:
            r, g, b = DRUM_COLOR[y]
            color = (r//2, g//2, b//2)  # this voice is enabled
            #print("Playing: ", VOICES[y])
            mixer.play(samples[y], voice=y)
            color = TICKER_COLOR     # no voice on
        trellis.pixels[(y, current_step)] = color

    # handle button presses while we're waiting for the next tempo beat
    # also check the accelerometer if we're using it, to adjust tempo
    while time.monotonic() - stamp < 60/tempo:
        # Check for pressed buttons
        pressed = set(trellis.pressed_keys)
        for down in pressed - current_press:
            print("Pressed down", down)
            y = down[0]
            x = down[1]
            beatset[y][x] = not beatset[y][x] # enable the voice
            if beatset[y][x]:
                color = DRUM_COLOR[y]
                color = 0
            trellis.pixels[down] = color
        current_press = pressed

            # Check accelerometer tilt!
            tilt = accelerometer.acceleration[1]
            #print("%0.1f" % tilt)
            new_tempo = tempo
            if tilt < -9:
                new_tempo = tempo + 5
            elif tilt < -6:
                new_tempo = tempo + 1
            elif tilt > 9:
                new_tempo = tempo - 5
            elif tilt > 6:
                new_tempo = tempo - 1
            if new_tempo != tempo:
                tempo = max(min(new_tempo, MAX_TEMPO), MIN_TEMPO)
                print("Tempo: %d BPM" % tempo)
                time.sleep(0.05)  # dont update tempo too fast!
        time.sleep(0.01)  # a little delay here helps avoid debounce annoyances

Sequence Tracking Bar

When the board restarts, it will play through all four sound files, and then start running the sequence tracking bar forward

There are many ways to conceptualize an eight step sequence. I like to think of it as a single measure with 4/4 timing, meaning there are four beats to the measure and a quarter note gets the beat. So, our seqeunce is made of eight eighth notes, and the steps can be counted as:


Program Beat Patterns

Now, you can start making up some beats using the samples!


Try pressing the first and fifth buttons on the bottom row to start up a kick drum on the first and third beats of the measure (remember, there's a beat on every other button pad, with syncopated subdivision between them).


Now we'll add in a snare on step 3 and 8, which are the second beat and the "and" after the fourth beat as shown here.


Now let's lay in hi-hats on nearly every beat, as shown. While the Trellis M4 can play multiple sounds simultaneously, sometimes patterns sound nicer when there isn't too much going on on every step!


Lastly, we'll throw in an electronic blip sound that's reminiscent of the "cowbell" on a Roland TR-808. We'll put it on the two and then on the eighth note divisions of the three and four.

Here is what the sequence sounds like with the 808 style voices:

And here it is at a higher BPM with some genuine Adafruit warehouse sounds! You can increase and decrease the tempo just by tilting the Trellis M4 to the right or left. The Analog Devices ADXL343 accelerometer built right on the board detects the rotation and the CircuitPython code translates that data into tempo changes.

Temporarily unable to load embedded content:
This guide was first published on Nov 18, 2018. It was last updated on Nov 18, 2018. This page (Eight-Step Drummer) was last updated on Jun 24, 2019.