Using expressions in Dilla

As programmers, we are primed to avoid repetition from the day we write our first subroutine. How can we apply the same principles when composing music in the browser with Dilla?

If you are not familiar with Dilla, a small library I made for scheduling notes, you should first have a look at my previous post: "Making a boombap beat with Dilla and the Web Audio API".

A blipping instrument

Since reading text on screen is hardly what makes Web Audio awesome, we'll need a simple instrument to represent our notes. We'll create a simple oscillator, using a waveshape and frequency based on the current value of beat and tick.

dilla.on('step', function onStep (step) {
  if (step.event !== 'start') return;
  // Create oscillator and gain, connect to graph   var oscillator = step.context.createOscillator();
  var gainNode = step.context.createGain();
  oscillator.connect(gainNode);
  gainNode.connect(step.context.destination);
  var position = step.args.position.split('.');
  var beat = position[1], tick = position[2];
  // Set oscillator type based on current beat   var types = ['sine', 'square', 'triangle', 'sawtooth'];
  oscillator.type = types[beat - 1];
  // Set frequency based on current tick   var freq = 220 + ((440 / 96) * tick);
  oscillator.frequency.value = freq;
  // Sustain 10ms, then release over 10 ms   gainNode.gain.setValueAtTime(1, step.time);
  gainNode.gain.setValueAtTime(1, step.time + 0.1);
  gainNode.gain.linearRampToValueAtTime(0, step.time + 0.2);
  // Start and stop oscillator   oscillator.start(step.time);
  oscillator.stop(step.time + 0.2);
});

Wildcard operator

The asterisk matches any number in a position. Assuming the default 4 beats per bar and 2 bars per loop at 120 bpm, the expression below would generate 8 notes: 1.1.01, 1.2.01, 1.3.01, 1.4.01, 2.1.01, 2.2.01, 2.3.01 and 2.4.01.

dilla.set('blip', [
  ['*.*.01']
]);

Play wildcard example

Odd and even operators

Matches odd or even numbers in a position. If we lower the tempo to 60 bpm, we can hear the frequency going up, and then switching to another waveform on the next beat.

dilla.setTempo(60);
dilla.set('blip', [
  ['*.*.odd']
]);

Play odd example

Modulus operator

With the modulus operator, you can set repetition in more fine-grained intervals. The expression below would generate 8 notes per beat: 1.1.01, 1.1.13, 1.1.25, 1.1.37, 1.1.49, 1.1.61, 1.1.73 and 1.1.85, and so on...

dilla.set('blip', [
  ['*.*.%12']
]);

Play modulus example

Combining several expressions is also possible, and Dilla will make sure there are no duplicate notes. You can also define an offset for the modulus operator. The expression below will match ticks 24, 42, 60 and 78 for odd beats and 1, 13, 25, 37, 49, 61, 73 and 85 for even beats.

dilla.set('blip', [
  ['*.odd.24%18'],
  ['*.even.%12']
]);

Play modulus offset example

Plain javascript

Since we're composing our tune with code, we could also just use plain javascript to add notes. Below is an example of some fun randomness.

function random (min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
} var goal = random(10, 100); var notes = []; var position; while (notes.length < goal) {
  position = [random(1, 2), random(1, 4), random(1, 96)].join('.');
  notes.push([position]);
} dilla.set('blip', notes);

Play random example

Wrapping up

That's just a few examples of how you can use expression operators in Dilla to avoid repetition when composing music in code.

Do you use other strategies for scheduling Web Audio events and creating interesting note patterns? Let's discuss it on Twitter.