Getting started

We can write and run code interactively in this worksheet.

Feel free to edit any of the code you see - you can always refresh the page to get the original worksheet back.

Links: // update these slides

This is how we write a basic pattern in Strudel.

  • sound is a function that tells strudel we want to play a sound
  • Inside the speech marks we write the names of the sounds we want to play
sound("bd hh sd oh")
  • You play the sound by pressing the play button or hitting ctrl and enter
  • You can stop the sound by hitting ctrl and . (for some people this doesn’t work…) or pressing the stop button
  • The sounds have to load so you might not hear them first time round

The more elements we put into the pattern the faster and denser it becomes - this is because Strudel has a consistent ‘cycle’ of time running in the background. Any sounds in the pattern will run within that cycle, and Strudel will try to space them evenly in time. (Unless we tell it not to… we’ll learn how to do that later).

Even more?

sound("bd hh sd oh hh bd oh sd hh sn oh bd")

This means that the pulse of the pattern is dictated by the number of elements - which gives us some nice opportunities to play with rhythm.

$: sound("hh bd sd")
$: sound("jazz jazz cp jazz")

Exercise - 5 mins

Now try writing your own pattern with some of these other sounds:

insect wind jazz metal east crow casio space numbers

//your code here

Pattern notation

The green writing inside the speech marks has its own set of rules for defining patterns. We call this the ‘mininotation’ and it’s the core of coding in Strudel.

We can speed things up with *

sound("hh*16 oh")

Or slow them down with / - the clap only sounds every other cycle:

sound("bd cp/2") 

We can choose different sounds from a set using :. The computer starts counting at 0, so casio:0 is the same as casio.

sound("casio casio:1 casio casio:5")

We can add a rest using ~

sound("casio casio:1 ~ casio:5")

We can create more variation with sub-sequences. We do this by breaking our steps down into mini patterns using []

sound("bd sd bd hh")
sound("bd sd [bd cp] [hh oh]")

You can nest sequences within sequences for dense patterns

sound("[[bd [bd bd bd bd]] bd sd] [bd cp]")

We can also schedule events across multiple cycles using <>

sound("casio <[hh cp] casio:1> ~ casio:5")

Let’s add some easy randomness using ?. Putting ? after a sound means there is a 50% probability that the sound will play.

sound("hh*8?")

Exercise: 10 minutes

  • Create your own patterns using the mininotation
  • Start with something simple and then add complexity bit by bit
  • Try to use all the elements outlined above
  • Don’t be precious about your code - if you’re not happy then delete it and start again!
//your code here

Chaining functions

Let’s learn about chaining functions together. This is how we start to make more complex patterns.

Let’s start with a function called note - this lets us re-pitch samples or choose different notes if we’re using a synth.

We connect functions together with .

note("0 1 0 5").sound("casio")

We can chain functions together in any order, however, because Strudel always takes the rhythm from the first pattern, we need to be careful how we do this:

sound("casio").note("0 1 0 5")

There’s another way of selecting samples, using a function called n - this gives us a bit more flexibility in writing patterns.

n("0 1 0 5").sound("casio")

This saves us a bit of typing, and makes things easier to edit as we go.

We can combine as many functions as we like:

note("0 1 0 5").sound("casio").n("<0 2>")

And we can make use of all the mininotation we learned above in the note or n pattern

n("0 1*3 <0 [~ 7]> 5").sound("casio <space jazz>")

Strudel does its best to map the two patterns together, based on when each event in the pattern starts.

Exercise - 5 minutes

  • Try writing a pattern using two or more functions
  • Try combining different pattern structures in your functions and see how they map together
//your code here

Pattern transformation

Let’s explore some ways of creating more complex patterns.

Old-skool videogame samples

But first we can load in some custom samples from old-skool videogames - NB. if you reload the worksheet you’ll need to re-run this cell to get the samples back.

samples('https://raw.githubusercontent.com/heavy-lifting/a-maze/main/strudel.json')
$: sound("segaoboe:2")

Here’s a list of the samples: asteroid track carnival defender dk frogger galaga joust nintendo pacman qbert chiphh chipbd segadrum segaviolin segaflute segavibra segasynth segapiano segaharps segahorn segaorgan segatrumpet segaclarinet segaoboe

We’ll start with a simple drum pattern:

n("9 10*2 9 [~ 2]").sound("segadrum")

We can slow that down with slow():

n("0 4*2 0 [~ 3]").sound("segadrum").slow(2)

Or speed it up with fast():

n("9 10*2 9 [~ 2]").sound("segadrum").fast(2)

You can change the number in brackets after fast or slow to change how much we speed up or slow down by. You can even pattern that number using the mininotation!

n("9 10*2 9 [~ 2]").sound("segadrum").slow("1 2")

It might seem like something a bit weird is happening here - basically Strudel is switching between two versions of the pattern, the normal version and the slow version. It can be a bit hard to get your head round exactly what’s happening sometimes, but as long as you’re happy with the sound I don’t think it matters too much.

Let’s think about some other kinds of patterns - rev() lets us play a pattern backwards:

n("0 <[1 2] 3> ~ 4").sound("asteroid")
n("0 <[1 2] 3> ~ 4").sound("asteroid").rev()

Or we can use every() to apply a transformation every so many cycles:

Notice here we are combining the functions in a slightly different way. The every() function takes two arguments, which are separated by a comma. The first one is the number of cycles to trigger the transformation, the second one is the transformation.

n("0 <[1 2] 3> ~ 4").sound("asteroid").every(4, rev())

Let’s listen to that with a drum beat underneath. We can play multiple patterns at once by starting each line of code with $:

$: n("0 <[1 2] 3> ~ 4").sound("asteroid").every(4, rev())
$: n("9 10*2 9 [~ 2]").sound("segadrum")

We can pass any function as the second argument to the every() function:

n("4 <[6 7] 8> ~ 10").sound("segapiano").every(4, slow(2))

Or we can do something similar with sometimes which will apply a transformation with 50% likelihood:

$: n("4 <[6 7] 8> ~ 10").sound("segapiano").sometimes(rev)
$: sound("bd(3,8)")

This can sound quite confusing as it’s randomly jumping between the forwards and backwards versions of the pattern. We can use someCycles() instead to choose once per cycle.

$: n("4 <[6 7] 8> ~ 10").sound("segapiano").someCycles(rev)
$: sound("bd(3,8)")

Iter starts the pattern at a different point each cycle, giving us a rotational pattern:

n("1 2 3 4").sound("numbers").iter(4).slow(2)

This can lead to some nice variation across time:

//drums:
$: n("9 10*2 9 [~ 1]").sound("segadrum").every(3, rev)
//synth:
$: n("9 <[10 1] 0> ~ 4")
.sound("joust")
.iter(4)

Finally, we can apply a transformation in one speaker only using jux:

n("9 10*2 <7 9> [~ 2]").sound("segaflute").jux(rev).slow(2)

Exercise - 10 minutes

  • Try writing some simple patterns and then applying transformations.
  • Remember - start simple and make iterative changes
  • Try creating some patterns in the parameters of your functions using the mininotation
  • If you’re feeling brave, use $: to run multiple patterns at the same time
//your code here

Making it sound more interesting

Long samples

If we play a long sample it will be triggered every cycle and so we will end up with an overlapping effect. This can sound good, but it’s not always what we’re looking for. Here are a few functions to help you work with longer sounds.

loopAt will loop the sample at a set number of cycles

sound("galaga").loopAt("1")

We can of course pattern that too:

n("3 4 5 6").sound("galaga").loopAt("<1 2>")

slice cuts the sample into equal slices and then lets us pattern how they are played back:

sound("galaga:2").slice(16, "5 3 1 [~ <2 7>]").iter(4)

We can also use legato() to truncate the sample - the parameter dictates how many steps the sample will play for

sound("frogger*4").legato(1)

Exercise - 5 minutes

  • Trying writing a drum beat and then work with a longer sample on top using loopAt or slice
  • Some longer samples and loops can be found in defender galaga qbert pacman nintendo frogger track
// your code here
// drums
$: 
// longer samples
$: 

Effects

Ok, let’s try adding some effects.

The vowel effect is a kind of filter.

n("6 <[9 7] 6> ~ 5").sound("segatrumpet").legato(1).vowel("a")

We can use mini-notation to create a pattern in the effect:

n("6 <[9 7] 6> ~ 5").sound("segatrumpet").legato(1).vowel("<a e>")
n("6 <[9 7] 6> ~ 5").sound("segatrumpet").legato(1).vowel("a e i")

Most effects take a pattern of numbers. pan moves the signal left and right:

n("6 <[9 7] 6> ~ 5").sound("segatrumpet").legato(1).pan("0 1")

room adds reverb:

sn("6 <[9 7] 6> ~ 5").sound("segatrumpet").legato(1).room("<0 0 1 2>")

We can chain effects together:

n("6 <[9 7] 6> ~ 5").sound("segatrumpet").legato(1).room("<0 0 1 2>").pan("0 1").vowel("<a o i>")

Exercise - 5 minutes

  • Take one of your patterns from above and try adding some effects
  • Try pattening the effect parameters
//your code here

Polyrhythm/polymeter

Health warning: I sometimes get these the wrong way round :s

We can use multiple patterns with $: or work within a single pattern using [,] to create polyrhythmic structures - in this case patterns of three over four

$: n("10 11 14 17").sound("chiphh")
$: n("12 18 22").sound("chipbd")
sound("[chiphh chiphh:3 chiphh:3, chipbd chipbd]")

We can use {,} to create polymetrical structures - the elements of the pattern after the comma will follow the same pulse as those before the comma This can give us a sense of rotation over time.

sound("{chiphh chiphh:6 [~ chiphh:14] chiphh:27, nintendo:17 ~ ~ ~ chipbd:29}")

If you’ve made it this far then well done!!! You can graduate to the main REPL and keep practicing - click go to REPL in the top right :)