Skip to content

Generate a Random Walk

A stream computes each cycle’s value from the previous one. Seed its random step with the cycle number, and the walk is the same on every playback.

Start the stream from a MIDI note, and add a step chosen from a list each cycle:

use "std/instruments" { SamplerMelodic, Kit };
use "std/generative" { stream };
use "std/random" { choose };
let steps = #[-2, -1, 1, 2];
let walk = stream(64, fn(prev, cycle) {
return prev + choose(cycle, steps);
});
let lead = AudioTrack("lead");
lead.set_instrument(SamplerMelodic(Kit("keys")));
lead << walk;
PLAY;

A stream works on MIDI numbers (60 = C4), so you can do arithmetic on the value directly. walk.query(n) returns the value at cycle n, to check a walk before you play it.

Turn the walk around when it would leave the range:

use "std/generative" { stream };
use "std/random" { choose };
let bound_steps = #[-3, -1, 1, 3];
let bounded = stream(60, fn(prev, cycle) {
let next = prev + choose(cycle, bound_steps);
if (next > 72) { return prev - 3; } // too high — turn around
if (next < 60) { return prev + 3; } // too low — turn around
return next;
});
print(bounded.query(8));

Compare rand(cycle) with a probability. Here the walk steps up 70% of the time:

use "std/generative" { stream };
use "std/random" { rand };
let rising = stream(65, fn(prev, cycle) {
if (rand(cycle) < 0.7) { return prev + 1; }
return prev - 1;
});
print(rising.query(3));

Start the stream from an array, and return an array:

use "std/generative" { stream };
use "std/random" { choose };
let voicing = stream(#[60, 64, 67], fn(prev, cycle) {
let step = choose(cycle, #[-2, 2]);
let result = #[];
for note in prev { result.push(note + step); }
return result;
});
print(voicing.query(2));