i  <esau

strudel is a browser port of tidalcycles

here are some notes from playing with it:

the foundational unit of time is a cycle. CPM = cycles per minute.

the default CPM is 120, which means that a cycle lasts 0.5 seconds.

any pattern written in quotes will play in one cycle

if you surround that pattern with angle brackets, each note will play in one cycle's length

The order you chain methods to set a pattern matters

// Creates a pattern with 3 events per cycle
note("1 2 3")
  // preserves this 3-event structure
  // uses the "piano" sound for each event
  .sound("piano");
open in strudel ↗
// Creates a pattern with 1 event per cycle
sound("piano")
  // only the "1" from "1 2 3" is used
  .note("1 2 3");
open in strudel ↗

calling setcpm() is a one-off thing. it doesn't reset if you delete the line and re-execute

there are lots of ways to do similar things with different methods, which causes concepts to blur quite a bit. mininotation is another thing.

using the "numbers" sample set is quite a useful way to understand how notes and rhythms are structured.

$: s("numbers").n("<0 1 2 3 4 5 6 7 8>");
open in strudel ↗

.begin() to start a sample later (i.e. to remove dead air)

.early() to offset a pattern for cool phases

you can use ribbon to create a buildup

all((x) => x.ribbon(1 / 2));
open in strudel ↗
then
all((x) => x.ribbon(1 / 4));
open in strudel ↗
then
all((x) => x.ribbon(1 / 8));
open in strudel ↗

Single quotes and double-quotes do different things

"1,2,3" (evaluated as a pattern) is different from '1,2,3' (evaluated as a string)

random snippets, techniques & scratches

cool beat

samples('github:switchangel/breaks');
samples('github:switchangel/pad');
samples('github:tidalcycles/Dirt-Samples');

setcpm(160 / 4);

$: s("breaks:3/2")
  .fit()
  .scrub(irand(16).div(16).seg(8).ribbon(2, 8))
  .o(4);

$: s("swpad:7")
  .scrub(irand(4).div(4).seg(2).ribbon(0, 2))
  .gain(0.5)
  .bpf(2000);

$: note("<C3 [E3 G3] A3 B3 D4>*[2, 2.01]")
  .scale("C:minor")
  .sound("piano")
  .gain(1)
  .room(1);

$: n(run(8).add(1))
  .sound("birds")
  .slow(10)
  .speed(0.7)
  .room(2)
  .gain(2)
  .delay(0.5)
  .delayspeed(0.1);

$: sound("bd*2")
  .bank("tr808")
  .gain(2)
  .duck(1)
  .o(2)
  .duckatt(0.5);

$: sound("<rd rd rd - - - >*12").gain(0.5).o(3);

$: sound("xylophone_soft_ff")
  .note("C3 D3 [B3 | A3 | G3]")
  .euclid(3, 5)
  .gain(2)
  .scale("C:major");

$: sound("xylophone_soft_ff")
  .note("<G1 [G2 | G4]>")
  .euclid(2, 7)
  .gain(2)
  .scale("C:major");

$: n("<4! 11 24 18>*3").sound("alphabet").hpf(2000).o(3);

$: sound("- [rim:1 | rim:2] rim:5 -")
  .o(4)
  .sometimes((x) => x.ply(3))
  .dist("2:.2")
  .hpf(1000)
  .room(0.5);

$: sound("hh*16")
  .gain(4)
  .lpf(sine.range(4000, 10000))
  .gain(0.5);

$: sound("sine")
  .note("<C2, E3, G2> | <F3, A3, C3>")
  .gain(4);

all((x) => x.hpf(1000));
open in strudel ↗

Arrange / stack

const pattern1 = sound("bd - - - bd bd - -");
const pattern2 = sound("bd - bd bd bd - - -");
const pattern3 = note("<C2 C3>*8");

stack(
  arrange([4, pattern1], [2, pattern2]),
  arrange([4, silence], [2, pattern3]),
);
open in strudel ↗

quick sample chopping

sound("numbers").fit().scrub(irand(8).div(8).seg(16));
open in strudel ↗

snare rush

sound("rim:2*16")
  .mask(irand(2).segment(8).rib(1, 1))
  .penv(irand(16).sub(8).seg(8).rib(0, 1))
  .patt(1)
  .decay(0.2)
  .dist("2:.2");
// or for full random
// .degradeBy(0.5)
open in strudel ↗

jai paul

$: note(
  irand(16)
    .scale("C:dorian")
    // or
    // .scale("<A:pentatonic, E:pentatonic>")
    .segment(8)
    .rib(6, 3)
    .sub(24),
)
  .sound("gm_celesta")
  .lpf(200)
  .mask(rand.segment(8).gt(0.3).rib(0, 2))
  .dist("4:.2")
  .ply("2")
  .delay(0.2);
open in strudel ↗

cool hats

setcpm(160 / 4);
samples('github:yaxu/clean-breaks');
$: sound("swat/2").fit().delay(1);
open in strudel ↗

footwork

setcpm(160 / 4);
samples('github:switchangel/pad');
$: s("[hh hh oh oh]*4").gain(0.8).dec(0.1);
$: s("bd, swpad:1")
  .begin(stack("0", rand.range(0, 0.5).seg(16).rib(2, 1)))
  .speed("<1!3 [.6 .7 .8 .9]>/4")
  .beat("0,6,12", 16)
  .fast(2)
  .dec(1)
  .rel(0.5)
  .dist("4:.3");
$: s("rim")
  .beat("16 | [16,22,[28,30]?]", 32)
  .gain("5")
  .speed(1.3)
  .dec(0.05)
  .hpf(400);
$: s("tr808_rim:2*16").mask(irand(2).seg(16).ribbon(0, 1));
open in strudel ↗

pads

$: s("gm_recorder")
  .note("<E3 G2 D3 G2>")
  .scale("G:pentatonic")
  .dist(2)
  .att(1 / 2)
  .rel(3 / 2)
  .room(1);
$: s("sine")
  .note("<E3 G2 D3 G2>".trans(-12))
  .scale("G:pentatonic")
  .gain(1)
  .att(1 / 2);
// $: note("<[B4 A4 D5]>")
$: note("<[B4 A4 D5], [D6 B5 A5]>")
  .slow(6)
  .s("harp")
  .scale("G:pentatonic")
  .dist(2)
  .rel(2)
  .dec(2)
  .penv("<4 -4>/2")
  .patt(1 / 4);
open in strudel ↗

busy looping melodies

const bSeed = "<0 0 0 3>".add("<2 1>/4");
const notes = note(
  perlin.range(0, 7).seg(16).rib(bSeed, 1).sub(6),
)
  // .mask(irand(2).seg(8).rib(2,1))
  .scale("C:major");

$: notes
  .sound("xylo")
  .decay(1 / 8)
  .transpose("12");
$: notes.sound("mallets").late(0).dec(0.1).release(0.1);

$: sound("hh*8").fit().gain(0.2).hpf(3000);
$: sound("bd*4").lpf(400);
open in strudel ↗

weird granular synth

$: sound("bd bd bd bd - bd - -")
  .ply(8)
  .freq(perlin.range(0, 1000));
open in strudel ↗

next part helicopter (drum over this?)

$: note("<C2!4 A0!4>*4".add(28))
  .sound("sine")
  .gain(2)
  .decay(1)
  .o(2);

$: note("<C2!4 A0!4>*4")
  .sound("sine")
  .gain(2)
  .decay(1)
  .o(2);

$: sound("bd*15")
  .lpf(slider(1000, 200, 1000))
  .duck(2)
  .duckatt(0.001);

$: sound("hh*8").hpf(2000);

$: sound("- - - sd").hpf(2000);
open in strudel ↗

dazed and confused and beading

$: note("<0 1 2 3 4 5 6 7>*2".add(30))
  .sound("sine")
  .revv()
  .release(2);

$: sound("rd - rd rd").bank("vintage").gain(0.2).swing(2);

$: sound("bd*2").bank("vintage");
open in strudel ↗

deacon

setcpm(70);

$: stack(
  sound("bd:3 -").lpf(180).dist("2:.3"),
).bank("vintage")

$: sound("hh*4").bank("vintage").gain("<.1 .2>*2").dec(.4)

$: sound("bd").slow(2).bank("vintage").duck(2).duckatt(.2)

$: sound("<vintage_rd:3 vintage_rd:2!2>*6").gain("<.2 .1@2>*6".add(slider(0,-0.3,4,.1)))

$: sound("triangles:2*6").mask(irand(2).seg(6).rib(1,4)).pan(rand.range(0,1)).delay(.3).dt(1).gain(4)

let notes = irand(8).seg(8).rib(20,200)

$: note(notes)
  .struct("<x x@2 x@3>*3")
  .sound("sax_vib")
  .dec(3)
  .scale("F#:pentatonic, C#:major")
  .o(2)

$: note(notes.sub(10)).struct("<x@2>").sound("sine").scale("F#:pentatonic")
open in strudel ↗

eyelash

samples('github:switchangel/breaks');
samples('http://localhost:5432');

setcpm(160 / 4);

const beat = "<1 0 0 1 0 1 0 0>*16";

// $: sound("breaks/2").fit().scrub(irand(16).div(16).seg(16).rib(2,1)).o(2).hpf(2000).gain(slider(1,0,2,.1))

$: sound("bd").dist(2).o(2);
$: sound("bd").struct(beat).lpf(200).dist(2).o(2);

$: sound("- sd").gain(2).speed(2.2).o(2);
// $: sound("- [sd [[sd sd sd] | ~ ] ]").gain(1.5).speed(2.2)
// $: sound("sd*16").bank("vintage").mask(irand(2).seg(16).rib(0,4)).gain(.3)

// $: sound("hh*16").gain("<.3 .5>*16").dec(.04).hpf(perlin.range(1000,4000))

// $: sound("<- hh hh hh>*16").dec(.1).gain("<.3 .7 .3>*16")

// $: sound("ocean:8")
//   .freq("<65 76>/2")
//   .struct(beat)
//   .begin(rand.segment(1).slow(2))
//    // .begin(rand.seg(8).rib(4,2))
//   .dec(3/8)
//   .room(.5)

// $: sound("saw").note("<[C4, D5] [C3, F5, A5]>/2").att(1).vib(4).vibmod(.2).release(2).gain(.5)

// $: sound("tri").duck(2).duckatt(10).duckdepth(1).gain(0).duckons(0).sus(10)
open in strudel ↗

i've been

setcpm(160 / 4);

$: sound("<ocean:3*2 [ocean:4 | ocean:3]>")
  .fit()
  .late(0.5)
  .room(1)
  .chop(8)
  .dec(0.2)
  .phaser(2)
  .gain(2)
  .o(2);

$: note("<[C,E,G] [A,C,E]>/4")
  .sound("tri")
  .tremdepth(2)
  .tremolosync("<8 12 16>")
  .trem(3)
  .tremshape("sine")
  .gain(3)
  .o(2);

$: stack(
  sound("bd*4").duck(2).duckatt(0.2),
  sound("hh*16").gain("<.3 .2 .3>*16"),
  sound("sd")
    .late(1 / 2)
    .sometimes((x) => x.delay(1.4))
    .dist("4:.3")
    .clip(1 / 16),
).bank("vintage");
open in strudel ↗

control keys

$: sound("hh*8").whenKey("Control:j", (x) => x.gain(2));
open in strudel ↗

fills

$: sound("hh*8").when("<0@3 1>", (x) => x.fast(2));
// or
$: note("E3*4")
  .sound("sine")
  .rel(0.1)
  .dec(0.5)
  .every(2, (x) =>
    x
      .euclid(7, 16)
      .note("A2 C3 D3 E3")
      .off(1 / 16, (x) => x.add(note(7))),
  );
// or override struct
$: note("E3*4")
  .struct("x - - x - - x - - x - - x - - x")
  .every(2, (_) => note("A2 C3 D3 E3").struct("<x ->*16"))
  .sound("sine")
  .rel(0.1)
  .dec(0.5);
open in strudel ↗

jungle gym

$: stack(
  sound("xylo*3")
    .note(irand(4))
    .scale("C4:minor:pentatonic"),
  sound("xylo*4")
    .note(irand(8))
    .scale("C4:minor:pentatonic"),
  sound("mallets")
    .note(irand(5))
    .scale("C2:minor:pentatonic")
    .lpf(600),
)
  .rib("<1 2 3 4>/2".add(4), "[0.5 | 0.75 | 1 | .25]/2")
  .gain(0.25);
open in strudel ↗

random chords

$: irand(12)
  .seg(8)
  .rib(0, 2)
  .add("[3,5,7] | [0,2,9]")
  .fast(4)
  .scale("C3:minor")
  .note()
  .struct("<x - - x - - - ->*16")
  .sound("triangle")
  .release(1);

$: sound("r8_rd*6").gain(0.2);

$: sound("<- hh hh>*12");

$: sound("<- sine!2>*12").note("[C2]").gain(4);

$: sound("bd").lpf(200).gain(2);

$: sound("akaixr10_tb*16")
  .mask(irand(2))
  .ply(2)
  .gain(rand.range(0.2, 0.4));

$: sound("gm_bird_tweet:3").delay(2).delayt(0.1).delayfb(2);
open in strudel ↗