Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Threads and Concurrency

Music has multiple parts playing simultaneously — drums, bass, melody, effects. Threads make this possible.

The Problem

Without threads, code runs sequentially:

# This plays ONE thing at a time
4.times do
  sample :bd_tek
  sleep 1
end

4.times do
  sample :sn_dub
  sleep 1
end

The snare only starts AFTER all kicks finish. That’s not music — that’s a sequence.

The Solution: in_thread

in_thread runs code in the background:

in_thread do
  4.times do
    sample :bd_tek
    sleep 1
  end
end

in_thread do
  4.times do
    sleep 0.5
    sample :sn_dub
    sleep 0.5
  end
end

Now both run simultaneously! Kicks on the beat, snares on the off-beat.

Thread Timing

Threads start at the same moment:

in_thread do
  puts "Thread 1 starts"
  sleep 2
  puts "Thread 1 ends"
end

in_thread do
  puts "Thread 2 starts"
  sleep 1
  puts "Thread 2 ends"
end

puts "Main continues immediately"

Output:

Main continues immediately
Thread 1 starts
Thread 2 starts
Thread 2 ends     # After 1 beat
Thread 1 ends     # After 2 beats

The Drum Pattern

Here’s how we build drums with threads:

define :drums do |k=1, s=1, h=1|
  # Kick thread
  in_thread do
    4.times do
      kick k
      sleep 1
    end
  end
  
  # Snare thread
  in_thread do
    sleep 1          # Wait for beat 2
    snare s
    sleep 1          # Wait for beat 4
    snare s
    sleep 2          # Fill remaining time
  end
  
  # Hi-hat thread
  in_thread do
    8.times do
      hat h
      sleep 0.5
    end
  end
  
  # IMPORTANT: Wait for pattern to complete
  sleep 4
end

The Final Sleep

The sleep 4 at the end is crucial:

# WITHOUT final sleep
8.times { drums }  # All 8 start immediately!

# WITH final sleep
8.times { drums }  # Each waits 4 beats before next

The main function must “take time” equal to the pattern length.

Visualizing Threads

Time:  1       2       3       4       
       |       |       |       |
Kick:  X       X       X       X
Snare:         X               X
Hat:   x   x   x   x   x   x   x   x

All three threads run in parallel, each following its own timing.

Complex Drum Patterns

The album uses more complex patterns:

define :drums_power do |k=1, s=1, h=1|
  in_thread do
    kick k; sleep 0.75
    kick k*0.6; sleep 0.25
    kick k*0.8; sleep 1
    kick k; sleep 0.75
    kick k*0.7; sleep 0.25
    kick k*0.9; sleep 1
  end
  
  in_thread do
    sleep 1.5
    snare s*0.7
    sleep 0.5
    snare s
    sleep 2
  end
  
  in_thread do
    8.times { hat h; sleep 0.5 }
  end
  
  sleep 4
end

This creates syncopated kicks with ghost notes.

Layering with Threads

Use threads to layer different elements:

# ARRANGEMENT SECTION
in_thread do
  12.times { drums 1, 0.9, 0.7 }
end

in_thread do
  12.times { bassline 1, 80 }
end

in_thread do
  sleep 16  # Wait 4 bars before melody enters
  8.times { melody 0.8 }
end

sleep 48  # Total section length: 12 bars

Thread Naming

For debugging, name your threads:

in_thread name: :drums do
  # ...
end

in_thread name: :bass do
  # ...
end

Common Mistakes

1. Forgetting the Final Sleep

# BAD - drums function returns immediately
define :drums do
  in_thread { 4.times { kick; sleep 1 } }
  in_thread { 4.times { snare; sleep 1 } }
  # No sleep 4!
end

8.times { drums }  # All start at once!

2. Mismatched Timing

# BAD - threads have different lengths
in_thread do
  4.times { kick; sleep 1 }  # 4 beats
end

in_thread do
  3.times { snare; sleep 1 }  # 3 beats - will drift!
end

3. Too Many Nested Threads

# AVOID - hard to track timing
in_thread do
  in_thread do
    in_thread do
      # ???
    end
  end
end

Keep thread structure flat when possible.

The Arrangement Pattern

Full tracks use threads for sections:

# INTRO
in_thread do
  8.times { drums 0.7, 0, 0.5 }
end
in_thread do
  with_fx :lpf, cutoff: 60 do
    8.times { bassline 0.6 }
  end
end
sleep 32

# MAIN
in_thread do
  16.times { drums 1, 0.9, 0.8 }
end
in_thread do
  16.times { bassline 1, 85 }
end
in_thread do
  sleep 16
  8.times { melody 0.8 }
end
sleep 64

Quick Reference

# Basic thread
in_thread do
  # Code runs in background
end

# Named thread
in_thread name: :my_thread do
  # ...
end

# Pattern with threads
define :drums do |k=1, s=1, h=1|
  in_thread do
    4.times { kick k; sleep 1 }
  end
  in_thread do
    sleep 1; snare s; sleep 1; snare s; sleep 2
  end
  sleep 4  # IMPORTANT!
end

Next: Effects — adding space, texture, and movement to sounds.