L-systems for Rhythm


L-systems are, according to Wikipedia:

a parallel rewriting system and a type of formal grammar. An L-system consists of an alphabet of symbols that can be used to make strings, a collection of production rules that expand each symbol into some larger string of symbols, an initial "axiom" string from which to begin construction, and a mechanism for translating the generated strings into geometric structures.

For a good example to visualise what this means, this was one I found very helpful.

I was inspired to start using L-systems for rhythm after hearing one of Renick Bell's Fractal Beats tracks on SoundCloud, and in turn reading his paper about rhythmic density in live coding for the Linux Audio Conference. The approach to rhythm in this Fractal Beats track is unlike any I have heard - the rhythms are complex and don't appear to lock into common divisions of a regular beat, but do not seem to fall into the trappings of being 'random'. This stochastic approach to rhythm appears to yield something interesting and that appears to 'evolve'.

While I have no idea how to use Conductive, there are some useful implementations of L-systems in SuperCollider. Prewrite is SuperCollider's class for implementing L-systems within patterns. Prewrite takes a rule set and an initial axiom, and will expand the axiom within a Pbind.

For example:

//L-systems basic example
//use L-system as a duration value for a kickdrum
(
l = Prewrite(1, // start with 1
        (    1: [0.25,2],
            0.25: [3,3,2]/4,
        3/4: [0.25,1,0.125,0.125],
        ), 4);
~k = Pbind(\instrument,\bplay,\buf,d["k"][0],\dur,l,\amp,1);
~k.play;
)
/*

With that grammar:

1 -> 0.25,2 -> 3/4,3/4,2/4 -> 0.25,1,0.125,0.125,0.25,1,0.125,0.125 -> etc.

*/
//much like with the euclidean rhythm convergence/divergence pattern, you can use variable l for different patterns too
(
~sn = Pbind(\instrument,\bplay,\buf,d["s"][0],\dur,l,\amp,1,\rate,Pseq((1..4)/2,inf));
~sn.play;
)
//and transform it
(
~h = Pbind(\instrument,\bplay,\buf,d["ch"][0],\dur,l,\stretch,Pwhite(0.5,2).round(0.5),\amp,Pwhite(0.2,1));
~h.play;
)
//an off-beat open hat for reference
(
~oh = Pbind(\instrument,\bplay,\buf,d["oh"][1],\dur,Pseq([0.5,Pseq([1],inf)],inf),\amp,1);
~oh.play;
)

When I use an L-system, I often pre-write it as writing them on-the-fly (essentially just writing a list) can be time consuming.

One advantage of the rewrite system is that individual rhythms that are complex can sound alone, without 'knocking off' the rhythmic structure, keeping the emphasis on the beat, while sounding rhythms that would be hard to insert using something like a Pwhite or would be a little more complex to write inside of a Pbind.

Take this L-system for example, which generates an array of random rhythms and uses them alongside self-similar structures:

(
var rhythm = Array.fill(rrand(4,10),{rrand(1,10)}).normalizeSum * rrand(1,4);
l = Prewrite(1,
    (
        //equal to 2 duration units/beats
        1: #[0.25,0.5,0.5,0.25,2],
        0.25: #[1],
        2: rhythm
),15);
//play a hi-hat with that L-system as a rhythm
~h = Pbind(\instrument,\bplay,\buf,d["ch"][0],\dur,l,\amp,0.8);
~h.play;
);

After some experimentation with trying to integrate L-systems within sets, I ended up recording the release HSPTLFLDHS (GitHub repo for that release can be found here)in October 2017, which exclusively uses multiple variations upon one L-system to create the entire set of rhythms across the release.

//L-system for HSPTLFLDHS
(
l = Prewrite(0.25,
    (
        0.25: #[0.25,0.25,0.25,0.5],
        0.5: #[0.25,0.5,0.125,0.125,0.125,0.125],
        0.125: #[0.375,0.125],
        0.375: #[0.375,0.375,1],
        1: #[0.75,0.25],
        0.75: #[16]
),60)
~k = Pbind(\instrument,\bplay,\buf,d["k"][0],\dur,l,\amp,1);
//play the L-system, listen for repetition!
~k.play;
);

The release uses a similar technique to the 'Convergence and Divergence' described in 3.4, but with the addition of a more organised system for multiplication and with the addition of rhythmic offsets. By using simple variations on one L-system over the course of two tracks the overall integrity of the rhythms are preserved, with recognisable self-similar patterns recurring, but with enough variation that it keeps my interest.

A simplified version of the HSPTLFLDHS setup will be included in the examples for this section. For more detail, see the HSPTLFLDHS repo.