Skip to content
Back home

Tutorial — Intermediate

Generate djent MIDI riffs with Python

The ideas behind djentgen: polymetric rhythm cells, low-string chugs, accents and a matching kick drum, written straight to a MIDI file you can drop into any DAW.
Reading time
40 min
Stack
Python 3.10+ · mido · MIDI
Published
3 November 2026

Djent is built on a trick that sounds complicated but is surprisingly mechanical: a short, oddly-sized rhythmic phrase repeated over a steady 4/4 beat until the two line up again. Bands like Meshuggah and Periphery made a sound out of it, and because it's mechanical, it's perfect for code.

I built djentgen to generate these riffs as MIDI, so I could drop them into a DAW and play along, or use them as a starting point for songs. In this tutorial we'll build a generator like it from scratch in Python, and you'll understand every note it writes.

If you just want to hear the result first:

pip3 install djentgen
djentgen --tempo-bpm 140 --num-measures 8

The rhythmic idea: polymeter

Most rock riffs repeat every bar, or every two bars. A djent riff often repeats every 7 sixteenth notes, or 9, or 13, while the drums keep a steady 4/4 pulse of 16 sixteenths per bar.

Picture a 7-step phrase laid over a 16-step bar:

Bar grid:  1 . . . 2 . . . 3 . . . 4 . . . | 1 . . . 2 . . . 3 . . . 4 . . .
Phrase:    x . x x - . . x . x x - . . x . | x x - . . x . x x - . . x . x x
           ^ phrase starts                   ^ bar 2 starts mid-phrase

Each time the phrase repeats, it lands at a different point in the bar. That constant shifting against a steady beat is what makes djent feel heavy and disorientating at the same time.

Left alone, a 7-step phrase and a 16-step bar only line up again after 112 steps, which is 7 bars. That's often too long to feel resolved, so most riffs reset the phrase at the start of a cycle of 2 or 4 bars. The listener gets the tension of the shifting phrase, then the release of everything landing together on the downbeat.

The sound: chugs on a low string

The other half of djent is the tone: palm-muted notes on a very low open string, usually a 7- or 8-string guitar tuned down.

  • Root note: the low open string, played most of the time
  • Accents: occasional higher notes, often dissonant intervals like the minor second or tritone
  • Dynamics: loud accents on the start of each phrase, and quiet ghost notes in between

In MIDI, notes are numbered 0 to 127. A standard guitar's low E is note 40. A 7-string in drop A goes down to A1, which is note 33. We'll use 33 as our default root.

MIDI in five minutes

A MIDI file isn't audio. It's a list of instructions: "start note 33 now, at this velocity", "stop note 33 a sixteenth note later".

The parts you need to know:

  • Ticks per beat: how finely a beat (a quarter note) is divided. We'll use 480, a common value, which makes a sixteenth note 120 ticks.
  • Delta time: each message stores the time since the previous message, not since the start. This trips up almost everyone the first time.
  • Velocity: how hard a note is played, from 1 to 127.
  • Channels: 16 per file. Channel 10 (index 9 in code) is reserved for drums by convention, which is how DAWs know to load a drum kit.
  • Tracks: a type 1 MIDI file can hold several tracks, which DAWs import as separate instruments.

Setup

We'll use mido, a small, well-maintained library for reading and writing MIDI files:

mkdir djent && cd djent
python3 -m venv .venv
source .venv/bin/activate
pip install mido

Your first chug

Here's the smallest useful program: a single sixteenth-note chug on the low A.

from mido import Message, MetaMessage, MidiFile, MidiTrack, bpm2tempo

TICKS_PER_BEAT = 480
SIXTEENTH = TICKS_PER_BEAT // 4

mid = MidiFile(ticks_per_beat=TICKS_PER_BEAT)
track = MidiTrack()
mid.tracks.append(track)

track.append(MetaMessage('set_tempo', tempo=bpm2tempo(140)))
track.append(Message('note_on', note=33, velocity=110, time=0))
track.append(Message('note_off', note=33, velocity=0, time=SIXTEENTH))

mid.save('chug.mid')

Drag chug.mid into any DAW, put a guitar or bass instrument on the track, and you'll hear one low note. Not much of a riff yet, but every part of the generator builds on these few lines.

Notice time=SIXTEENTH on the note_off: it means "a sixteenth note after the previous message", which is the note_on.

What's next

In the full tutorial we build the complete generator:

  • generating random phrases from rhythm cells
  • resetting the phrase on the cycle so the riff resolves
  • giving each phrase a fixed voicing so it sounds like a riff, not noise
  • accents and ghost notes with velocity
  • a kick drum that locks to the guitar, plus a half-time snare
  • a command-line interface with reproducible seeds

Checking access…

More tutorials