Procedural Music in Orca

I recently stumbled on the Orca sequencer, developed by Hundred Rabbits. It bills itself as an ‘esoteric programming language’ for writing music. It is purely a sequencer, and has good integration for MIDI and other output types which I haven’t explored… There are some pretty good tutorials out there already, but I wanted to illustrate a few ‘idioms’ I settled into during a weekend of experimenting with the language. It’s a great language for creating simple home-brew ‘trackers’ for controlling synthesizers.

The thing I’ve always really wanted more of in sequencers is the ability to inject ‘interesting randomness,’ such as varying a note’s gain/velocity randomly, or choosing notes from a given scale at random. SunVOX (my other favorite tracker of the 2010’s) supports random gain, but not much else AFAICT.

In this post, I’ll give a bit of intro/overview of Orca instructions, and document some recipes I came up with for making a slowly mutating bass/melody pattern. Here’s a video of the Orca programming running; it’s controlling my (terribly underutilized) OP-Z.

Orca Basics and Overview

The Orca language is much more like a spreadsheet than python. It’s a grid of ASCII text, and each individual letter is like its own spreadsheet cell. Cells usually contain either commands or data.

A good example of a command is the ‘Add’ operator, given by ‘A’: It reads the values from the left and right, and outputs the sum of the two below. This already gives a sense of how the geometry of a program is going to matter: You need space to add things. Chaining together many operations often requires some careful choices of layout. You can use Variables (the ‘V’ operator) to store values or read them out elsewhere, and use the Jumper operator (‘J’) to move a value straight south, which can be helpful for space management.

Another helpful pair of operators for space management is the Query (‘Q’) and ‘Generator (‘G’) which read and write multiple adjacent values at once. They refer to the read/write positions by relative offset, which makes copy-pasting blocks difficult. (For example, Q845 means “Read five values starting from the cell 8 to the right and 4 down”.)

Once the spreadsheet analogy is in mind, a few sharp edges become apparent. For example, managing relative offsets is hard if you want to duplicate a command block: the new placement has to be exactly right or Strange Things will happen. Another sharp edge is that Variables update from left-to-right, top-to-bottom. So, we can’t declare a variable in the lower-right edge of the screen and then use it in the upper-left. This leads to subtle bugs, and requires shifting things around occasionally.

One other shortcoming: After a bit of playing around, one is very tempted to start treating commands like data. For example, maybe we can store a Halt (‘H’) command in a variable, and then write it out elsewhere to stop an instrument. This doesn’t seem to work reliably.

Oh, and one more quirk: All numeric values are (unsigned) integers mod 36! Because there are 10 digits and 26 letters. This is hilarious and weird, and means you have ~5.2 bits of resolution for any continuous value. You’re mostly using these for control values, rather than sample values so it seems to be fine in practice.

Overall, though, the language is extremely simple. There’s definitely a bit of learning curve, but also a lot of joy in discovering cool tricks.

Screenshot of an Orca program running. Like a spreadsheet, the code and data are mixed together. Like Nethack, every individual character means something, and your current cursor position is marked by an ‘@’ sign.

Recipes for Randomness

My overall goal was to create a randomly ‘mutating’ bass/melody line.

For example, consider a 16-beat pattern where we just play random notes (or no note at all) at each time step. At one extreme, we can choose a random note EVRY time step and never repeat ourselves – but this isn’t very ‘musical.’ At another extreme, we can conjure up 16 random notes and play the pattern over and over again forever. This makes the random pattern much more musical, but will quickly become very boring. Instead, I wanted to create a random melody where the notes are intermittently updated. Then the pattern “repeats” forever, but it changes slowly over time, which keeps things at least mildly interesting.

How do we do this in Orca? First, lemme say that I think there’s always at least three ways to do anything in Orca; this is just the best I’ve come up with so far.

Random Notes from a Pentatonic Scale

The first nice trick is to draw from a random distribution using the R, T and V operators. The Track operator (‘T’) creates a track of length L, and outputs the value at position k within the track.
So: “3cT123456789abc” will output a ‘3’ below the T. You can put any kind of data in the Track, like numbers or notes. We can the produce a random position value using the Random operator to read a random value from the track. “0Rc” will produce a random value between 0 and c every time step, which will let us read a random value from the Track.

To bring it all together, this snippet creates a D pentatonic scale, with a preference for the base note, and a 1/3 chance of outputting no note at all. The output note is stored in a variable named ‘n‘.

0Rc.............
.3cTDDDDFGAC....
.nVD............

The scale and relative frequency of the notes can be easily changed by changing the ‘track.’

The Midi Output operator ‘:’ takes many arguments. To the right, it looks for the MIDI instrument number, note, octave, velocity (gain), and note length. To the left, if a ‘*’ is present, the note is triggered.

I’m uuusually working with one MIDI instrument at a time. But we can make separate random Tracks (just as we did for the pentatonic scale) to choose the octave, gain and maybe note length. Just like with the scale, we can set it up so that (say) octave 4 is the most likely, with some chance of picking octave 2 or 5.

Finally, suppose we stored the randomly chosen note in ‘n’, octave in ‘k’, and velocity in ‘v’. To play the random note, we can use the konkat operator (‘K’) which reads multiple variables, and outputs their values directly beneath each chosen variable. So, ‘3Konv’ says we’ll read three variables, and outputs the values of ‘o’, ‘n’, and ‘v’. Then the following snippet will output the complete random instruction to MIDI:

....3Konv..
......4Dg..

..1U4.JJJ..
...*:14Dg..

The ‘1U4’ is a eUclidean operator, which will output a ‘*’ (or ‘bang’) once every four time steps, triggering the MIDI note. The Jumper operator (‘J’) just copies what’s above to what’s below; we place them here so that the ‘K’ left input doesn’t overlap the ‘C’ input. (Remember what we said about carefully managing space?)

Playing a Pattern

Usually we’ll want to play a fixed sequence of notes. This is called a ‘pattern’ in the world of tracker sequencers. To do this, we make a data block inside comments (‘#……#’) with all the note information we want. Then we use the Query operator (‘Q’) to read one note’s information from the pattern. Q reads a specified length of data from the specified offset (‘Q813’ will read three values from 8 spaces to the right and 1 space down.) Then we just need to increment the ‘y’ offset in the Query to read a pattern sequentially. The Clock operator (‘C’) does exactly that.

So, our final pattern reader looks like this:

1I1.Q523.......
.*:14Dgg#4Dg#..
........#4Dg#..
........#4Dg#..
........#4Dg#..

Mutating Pattern

But I don’t just want to play a random note; I want a sequence of notes which slowly mutates over time. It turns out this was relatively hard to figure out. The ‘nicest’ approach I found worked like this:

  • Every time step, we produce a random octave, note, and velocity, as discussed above.
  • We create two side-by-side patterns, one of which will be the ‘real’ pattern; the other is a ‘trash’ pattern.
  • Every time step, we pick a new random octave+note+velocity (which I’ll just call a ‘note’ for short), which we’ll write into one of the two patterns.
  • We also pick a random x-offset (from two possible choices) and y-offset for where to write the random note; with a 1-in-8 chance, we place it at a random position in the ‘real’ pattern, and with a 7-in-8 chance we place it in the ‘trash’ pattern. The y-offset can be any step in the pattern; I also set this up to be a lopsided distribution, so that the ‘big’ steps at 0, 4, 8, 12 are more likely to be picked than the in-between steps.
  • A player reads sequentially through the ‘real’ pattern playing each note it encounters. The trash pattern is ignored.

This works just fine; it’s the main idea in the ‘mutations‘ video. Because we usually write to the trash pattern, the ‘real’ pattern only slowly updates, and we get a sense of persistent melody. The main drawback really is that the ‘trash’ pattern takes a lot of extra space.

Conclusions

Orca is delightful and weird. It’s really cool having a domain-specific language for defining synth sequencers; it opens up a lot of possibilities that normally just aren’t there in a ‘normal’ tracker/sequencer. The overall small footprint of the language means that – even though it’s quirky – it’s still pretty quick to pick up. There’s very little overhead!

There’s certainly lots of remaining nice-to-haves, like being able to move around ‘Halt’ operators in variables, or having some better options for control flow. But on the whole, it’s a really delightful little language, and I spent a couple whole weekends tinkering with it. Super fun!