# Walker

This example shows how to introduce variation to our music through the idea of a random walk.

In 2D graphics, a random walk involves drawing a path by repeatedly choosing a random direction in which to move. On this 2D plane, each step can be one of 4 directions (up, down, left, right, or 5 if you count not moving). Given a certain set of rules, this could look as follows:

How might we apply this idea to music? We know from our music primer that a piano has 88 keys, giving it a range from A0 to C8, which map to the MIDI numbers 21–108. We can treat this as a 1D plane, and say that at each moment we have 4 choices: 1) play a higher note; 2) play a lower note; 3) play the same note; 4) play nothing. The range 21-108 and the 4 choices define the "possibility space" for our random walk.

## Random notes

Let's start simple and just play random notes by repeatedly picking a random number between 21 and 108. This gives us a random distribution, where each of the 88 notes have an equal chance of being played.

For this example, we'll need a way to generate a random number within a given
range, which JS doesn't have out-of-the-box. For this we'll use
`lodash`

, which provides a bunch of
useful functions we'll use throughout the book. See the
JavaScript chapter for help installing modules.

```
import { random } from 'lodash';
import { instrument, metronome, midi } from 'gem';
midi().then(output => {
const metro = metronome(10);
const inst = instrument({ output, metro });
// Play a random note between 21 (A0) and 108 (C8)
metro.onTick(() => inst(rand(21, 108)));
metro.start();
});
```

## Listen

This probably sounds like the bleeps and bloops you imagine when you hear the phrase "computer generated music". At strict intervals and constant velocity the results sound rather mechanical.

## More randomness

By also applying some randomness to the timing and velocity, we can add more nuance.

```
import { instrument, metronome, midi } from 'gem';
const rand = (min, max) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
midi().then(output => {
const metro = metronome(10);
const inst = instrument({ output, metro });
// Play a random note, at a random velocity, for a random length of time
metro.onTick(() => inst(rand(21, 108), rand(0, 127), rand(250, 2000)));
metro.start();
});
```

## Listen

This makes things feel more natural, emulating the variations of timing and touch of a human player.

Whilst initially quite interesting though, pure randomness doesn't hold our attention for long, as, by definition, it lacks the patterns and structure that are a key aspect of our enjoyment of music.

## Relative notes

If we think about taking a walk, even if we're just wandering, we're not taking random steps and leaps. Our movements have direction, each step is related to the previous ones to keep us moving along a certain path.

Rather than choosing randomly, we can pick our next note relative to the current one.

```
navigator.requestMIDIAccess().then(midi => {
const outputs = midi.outputs.values();
let output = outputs.next().value;
function random(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function playNote(note, length, velocity) {
let noteOn = 0x90; // 144 = channel 1 note on
let noteOff = 0x80; // 128 = channel 1 note off
output.send([noteOn, note, velocity]);
output.send([noteOff, note, velocity], window.performance.now() + length);
}
let startNote = random(21, 108);
let startVelocity = random(0, 127);
let step = 5;
function play(note, velocity) {
let nextNote = random(
Math.max(note - step, 21),
Math.min(note + step, 108)
);
let length = 500;
let nextVelocity = random(
Math.max(velocity - step, 0),
Math.min(velocity + step, 127)
);
let timer = setTimeout(() => {
playNote(nextNote, length, nextVelocity);
clearTimeout(timer);
play(nextNote, nextVelocity);
}, length);
}
play(startNote, startVelocity);
});
```

## Listen

This gives us something that has more of a flow and sense of direction. We've constrained the options from all possible notes, to just those that are 5 notes (a 5th) either up or down from the current note. This gives us some control, but we're still very much at the whim of chaos to determine our path.

## Probability

By applying probability, we can still employ randomness, but weigh the odds to favour specific outcomes. Adjusting the weights, we can influence how our program behaves.

```
navigator.requestMIDIAccess().then(midi => {
const outputs = midi.outputs.values();
let output = outputs.next().value;
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function playNote(note, length, velocity = 127) {
let noteOn = 144; // channel 1 note on
let noteOff = 128; // channel 1 note off
output.send([noteOn, note, velocity]);
output.send([noteOff, note, velocity], window.performance.now() + length);
}
let startNote = random(21, 108);
let length = 250;
function play(note) {
let prob = 0.4;
let num = Math.random();
let nextNote;
if (num < prob) {
// 40% chance of going down 7 steps
nextNote = Math.max(note - 7, 21);
} else {
// 60% chance of going up 5 steps
nextNote = Math.min(note + 5, 108);
}
let timer = setTimeout(() => {
playNote(nextNote, length);
clearTimeout(timer);
play(nextNote);
}, length);
}
play(startNote);
});
```

## Listen

Here, we've weighed the odds to favour going up the scale. We'll sometimes dip downwards, but the results will always trend upwards over time.

This is the first example where we start to see some of the creative decisions and tradeoffs we can make, e.g. how do we balance things so we don't always end up playing only the highest notes?

## Normal distribution

Probability is one way to reign pure randomness. Another way is to emulate a common pattern found in nature, where values tend to cluster around a certain range, otherwise known as normal (or Gaussian) distribution (in contrast to pure randomness, which aims for uniform distribution). This maps well to music, where melodies tend to use a narrow range of notes and steps.

```
import random from 'random';
import clamp from 'lodash';
import { instrument, metronome, midi } from 'gem';
midi().then(output => {
function playNote(note, length, velocity) {
let noteOn = 144;
let noteOff = 128;
output.send([noteOn, note, velocity]);
output.send([noteOff, note, velocity], window.performance.now() + length);
}
// A mean of middle C with a small deviation
let noteGen = random.normal(60, 3);
// A mean of half velocity with a large deviation
let velGen = random.normal(64, 20);
let length = 200;
setInterval(() => {
// We need to 'clamp' these values to prevent them straying out of range
let note = clamp(Math.round(noteGen()), 21, 108);
let velocity = clamp(Math.round(velGen()), 0, 127);
playNote(note, length, velocity);
}, length);
});
```

## Listen

Here, the notes cluster around middle C and medium velocity. By increasing the deviation from the mean we can introduce more variation.

- Perlin noise
- Notes from scale
- Intervals?