Sonic L-Systems


In 1968, Aristid Lindenmayer introduced "L-systems" to the world. Basically, this is an iterative rewriting system of rules that operates on a string beginning with an "axiom" or initial string.

Using turtle graphics it is possible to draw all manner of fractal "pathological curves" with this technique.

Being a music nerd, I thought, "Why not make this play notes instead of draw lines?" And so the program [ lindenmayer-midi] was born.

The program is short but has a few parts. The first is the preamble that says we are a perl program and that we will be fooling with MIDI things:

#!/usr/bin/env perl
use strict;
use warnings;

use MIDI::Util;

Next, the program takes arguments from the command-line user:

my $rule       = shift || 2,
my $iterations = shift || 4;
my $string     = shift || 'F';
my $distance   = shift || 'qn';
my $theta      = shift || 1;

These variables specify the rule to use (shown below), the number of iterations to perform, the initial string (axiom), the "distance" – a musical duration like the quarter note, and theta – the amount to increase/decrease the current note value by.

Next up is to define the actual re-write rules to use:

my %rules = (
    # Sierpinski arrowhead curve: start=F
    5 => {
        F => 'G-F-G',
        G => 'F+G+F',

The program then initializes a MIDI score and sets the initial note to middle C (MIDI note 60):

my $score = MIDIUtil::setup_midi( patch => 0, bpm => 300 );
my $note = 60;

Ok. Now for the meat of the program – a dispatch table of MIDI and note events, re-writing the string according to the given rules, and finally translating each string symbol into a dispatched command:

my %translate = (
    'f' => sub { $score->r($distance) },
    'F' => sub { $score->n( $distance, $note ) },
    'G' => sub { $score->n( $distance, $note ) },
    '-' => sub { $note -= $theta },
    '+' => sub { $note += $theta },

for ( 1 .. $iterations ) {
    $string =~ s/(.)/defined($rules{$rule}{$1}) ? $rules{$rule}{$1} : $1/eg;
warn "$string\n";

for my $command ( split //, $string ) {
    $translate{$command}->() if exists $translate{$command};

Lastly, the program writes the MIDI file that was created.

$score->write_score( $0 . '.mid' );

Here are some examples. They are decidedly not music; more like Metroid on crack.

MIDI files: Sierpinski and Koch-islands-and-lakes. And here is an MP3 rendering of the former:

(On YouTube: , , , )

Not the easiest to dance to…