Get Started

Building Textured Algorithmic Music

A step-by-step guide to Roxanne Harris' approach to creating layered, algorithmic compositions in Sonic Pi

Understanding the Approach

Before we dive into code, understand this key concept: The processing is your composition, not the samples. You're designing systems that transform sound, and the samples are just raw material feeding through your creative algorithms.


Start Simple

Just play a sample to hear what we're working with:

sample :loop_safari

What's happening: You're hearing the raw sample in its entirety. This is your source material.

Add Structure and Timing

Add a metronome and live loop structure:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  sample :loop_safari
  sleep 1
end

What's happening:

  • use_bpm 80 sets the tempo
  • :met is your metronome keeping time
  • sync: :met makes sure everything stays in time
  • sleep 1 plays the sample every beat

Listen: You now have a structured, timed repetition.

Add Euclidean Rhythms

Control when the sample plays using mathematical patterns:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  sample :loop_safari,
    on: spread(4,16).look # 4 hits spread across 16 steps
  sleep 1
end

What's happening:

  • tick advances through a pattern each loop
  • spread(4,16) creates a euclidean rhythm - 4 hits evenly distributed across 16 steps
  • on: only plays the sample when the pattern is true

Listen: Instead of every beat, you hear 4 strategically spaced hits across a 16-beat cycle.

Visual tip: Try swapping to a shorter sample like :drum_bass_hard to hear the pattern more clearly, or count along to hear the spacing.

Add Granular Slicing

Now let's slice the sample and play different parts:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    start: line(0, 1, steps: 8).look, # slice the sample into 8 parts
    beat_stretch: 16, # stretch sample to fit 16 beats
    on: spread(4,16).look
  
  sleep 1
end

What's happening:

  • start: line(0, 1, steps: 8).look creates 8 start points from 0% to 100% through the sample
  • beat_stretch: 16 stretches the sample to exactly 16 beats long
  • Each hit now plays a different slice of the sample

Listen: You're hearing different textural chunks of the sample, creating variation within the pattern.

Speed Up the Action

Let's hear this at a more musical pace:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    start: line(0, 1, steps: 8).look,
    beat_stretch: 16,
    on: spread(4,16).look
  
  sleep 0.25 # 4 times faster
end

What's happening: sleep 0.25 means the loop runs every quarter beat, so you cycle through patterns much faster.

Listen: The same pattern but at a more musical pace.

Try switching to :loop_garzul or another sample to hear how the same process creates different textures.

Control Sample Envelope

Add envelope controls to shape how samples fade in and out:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    start: line(0, 1, steps: 8).look,
    amp: 1.5, # louder
    sustain: 0, # no sustain phase
    release: 0.125 * 3, # 3x the sleep time for overlap
    beat_stretch: 16,
    on: spread(4,16).look
  
  sleep 0.125 # even faster
end

What's happening:

  • amp: 1.5 makes it louder
  • sustain: 0 means no held portion - samples start fading immediately
  • release: 0.125 * 3 creates a fade-out that's 3x longer than our sleep time
  • This creates overlapping samples that blend together smoothly

Listen: Instead of choppy individual hits, you get flowing, overlapping textures.

Using bt() Function

Alternative way to calculate timing relationships:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    start: line(0, 1, steps: 8).look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3, # bt() converts to beat time
    beat_stretch: 16,
    on: spread(4,16).look
  
  sleep 0.125
end

What's happening: bt(0.125)*3 is another way to express timing in beat terms. Functionally the same as the previous step.

Add Shuffle for Controlled Chaos

Randomize the order of sample slices:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    start: line(0, 1, steps: 8).shuffle.look, # randomize slice order
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    on: spread(4,16).look
  
  sleep 0.125
end

What's happening: .shuffle randomizes the order of the 8 sample slices, but keeps the same 8 values. It's controlled chaos - familiar elements in unexpected order.

Listen: More unpredictable but still coherent textures.

Shuffle the Rhythm (Warning: This Gets Chaotic!)

Now let's shuffle when things happen too:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    on: spread(4,16).shuffle.look # shuffle the timing too
  
  sleep 0.125
end

What's happening: Now both the sample slices AND the timing pattern are shuffled. This creates maximum unpredictability.

⚠️

Listen: This sounds quite chaotic! The randomized timing loses the musical flow. This shows why we need better randomness control...

Introduce Seeded Randomness - The Solution

Replace chaotic shuffling with controlled, organic randomness:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0), # controlled randomness
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    on: spread(4,16).shuffle.look
  
  sleep 0.125
end

What's happening:

  • use_random_source :perlin uses smooth, natural-feeling randomness (like terrain generation)
  • use_random_seed 0 makes it repeatable every time you run the code
  • t: is a placeholder parameter - it doesn't affect sound but sets up the random state

Experiment: Try cycling through different random sources - :white, :pink, :perlin - and hear how they feel different. Perlin tends to sound most musical.

Apply Randomness to Timing

Use the seeded randomness to control when samples play:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0), # apply randomness to timing
    on: spread(4,16).shuffle.look
  
  sleep 0.125
end

What's happening: We're applying the same randomness to timing decisions. The rhythm becomes more organic while staying musical.

Note: This still sounds a bit boring and predictable - we need more syncopation...

Add Syncopation

Change to a more interesting rhythm pattern:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look # 7 hits instead of 4 creates syncopation
  
  sleep 0.125
end

What's happening: spread(7,16) creates 7 hits across 16 steps. This creates natural syncopation - a rhythm that feels like it's dancing around the beat rather than sitting squarely on it.

Listen: Much more alive and rhythmically interesting!

Layer in a Simple Kick Drum

Start building layers using the same approach:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look
  
  # Add a simple kick pattern
  sample :bd_fat, on: spread(1, 4).look
  
  sleep 0.125
end

What's happening: We add a kick drum playing once every 4 steps - a simple, steady foundation under our textural loop.

Switch to Better Kick and Add Processing

Upgrade the kick drum with better sound and processing:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look
  
  sample :bd_808, # punchier 808-style kick
    on: spread(1, 4).look,
    compress: 1, # heavy compression for punch
    pre_amp: 5 # boost before compression
  
  sleep 0.125
end

What's happening:

  • :bd_808 is a punchier kick drum sample
  • compress: 1 adds heavy compression for that modern punchy sound
  • pre_amp: 5 boosts the signal before compression, creating more aggressive compression

Make Kick Rhythm More Interesting

Give the kick a more complex pattern:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look
  
  sample :bd_808,
    on: spread(3, 8).look, # 3 hits across 8 steps - more interesting pattern
    compress: 1,
    pre_amp: 5
  
  sleep 0.125
end

What's happening: spread(3, 8) creates 3 kick hits across 8 steps, giving us a more complex but still musical kick pattern.

Add Randomness to Kick

Apply the same randomness approach to the kick drum:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look
  
  sample :bd_808,
    t: (use_random_source :dark_pink; use_random_seed look), # different random source
    on: spread(3,8).shuffle.look, # add shuffle for variation
    compress: 1,
    pre_amp: 5
  
  sleep 0.125
end

What's happening:

  • use_random_source :dark_pink gives the kick a different randomness character than the main loop
  • use_random_seed look means the randomness changes as the tick advances, creating evolution
  • .shuffle on the kick pattern adds more variation

Add Hi-Hats (Quiet at First)

Layer in hi-hats - you might not hear them clearly at first:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look
  
  sample :bd_808,
    t: (use_random_source :dark_pink; use_random_seed look),
    on: spread(3,8).shuffle.look,
    compress: 1,
    pre_amp: 5
  
  # Add hi-hats - might be hard to hear at first
  sample :hat_bdu,
    t: (use_random_source :light_pink; use_random_seed look), # third random source
    on: spread(4,8).shuffle.look, # different pattern from kick
    lpf: 1, # low-pass filter (we'll develop this more)
    amp: 0.75 # quieter than other elements
  
  sleep 0.125
end

What's happening:

  • :light_pink gives hi-hats their own randomness character
  • spread(4,8) creates a different pattern than the kick
  • lpf: 1 applies low-pass filtering (very subtle here)
  • amp: 0.75 keeps them in the background

Note: The hi-hats might be hard to hear in the mix - this is normal. We'll make them more prominent next.

Dynamic Hi-Hat Filtering

Make the hi-hats more interesting with evolving filter effects:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  
  sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0, 1, steps: 8).shuffle.look,
    amp: 1.5,
    sustain: 0,
    release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look
  
  sample :bd_808,
    t: (use_random_source :dark_pink; use_random_seed look),
    on: spread(3,8).shuffle.look,
    compress: 1,
    pre_amp: 5
  
  sample :hat_bdu,
    t: (use_random_source :light_pink; use_random_seed look),
    on: spread(4,8).shuffle.look,
    t: (use_random_source :light_pink; use_random_seed look % (16*4)), # longer cycle
    lpf: line(110,118, steps: 4).shuffle.look, # evolving filter
    amp: 0.75
  
  sleep 0.125
end

What's happening:

  • look % (16*4) creates a longer 64-step cycle for some parameters
  • lpf: line(110,118, steps: 4).shuffle.look creates a low-pass filter that moves between 110Hz and 118Hz
  • The filter frequency changes in shuffled order, creating evolving timbral changes
  • The modulo operation means some changes happen on longer cycles than others

Listen: The hi-hats now have a subtle but evolving character as the filter frequency changes.


Final Result: Complete Textured Composition

You now have a complete three-layer algorithmic composition:

Complete Code Structure:

use_bpm 80
 
live_loop :met do
  sleep 1
end
 
live_loop :f1, sync: :met do
  tick
  s = sample :loop_safari,
    t: (use_random_source :perlin; use_random_seed 0),
    start: line(0,1, steps: 8).shuffle.look,
    amp: 1.5, sustain: 0, release: bt(0.125)*3,
    beat_stretch: 16,
    t: (use_random_source :perlin; use_random_seed 0),
    on: spread(7,16).shuffle.look
  
  sample :bd_808,
    t: (use_random_source :dark_pink; use_random_seed look),
    on: spread(3,8).shuffle.look,
    compress: 1, pre_amp: 5
  
  sample :hat_cab,
    t: (use_random_source :light_pink; use_random_seed look),
    on: spread(4,8).shuffle.look,
    t: (use_random_source :light_pink; use_random_seed look % (16*4)),
    lpf: line(110, 118, steps: 4).shuffle.look,
    amp: 0.80
  puts s.args
  sleep 0.125
end

Next Steps: Experimentation

Try These Experiments:

  1. Swap samples: Try different source materials with the same processing
  2. Adjust spread patterns: Experiment with different euclidean rhythms
  3. Change random sources: Each source (:perlin, :pink, :white, etc.) has different character
  4. Modify timing relationships: Try different sleep values or release times
  5. Add effects: Experiment with reverb:, echo:, distort: parameters
🎯

Remember: You're not making music with samples - you're making music with processes that transform samples into something entirely new.


Related Topics

WIP