← Mike Higgins Home
Scrapbook Note •

Strudel

Strudel, a JavaScript version of Tidal Cycles.


https://tinyurl.com/298b4p66

// --- samples ---
await samples({ gtr: 'gtr/0001_cleanC.wav' }, 'github:tidalcycles/Dirt-Samples/master/')
samples('github:eddyflux/crate')

// --- tempo & feel ---
setcpm(25/4)                       // slow, funky
let swingMask  = "<0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1>/16"
let microSwing = 0.02              // subtle shove on offbeats

// --- simple sound wrappers (only Strudel-safe ops) ---
const lead8   = p => p.s("square").gain(0.65).crush(6).lpf(1600).delay(0.25).delayfb(0.25)
const bass8   = p => p.s("square").gain(0.95).lpf(750)
const pad8    = p => p.s("square").gain(0.35).lpf(1200)
const gtrStab = p => p.s("gtr").gain(0.45).room(0.6)

const rand = Math.random

// === KICK: seeded arrays (no .split) ===
const seedKickGrids = [
  ["bd","~","~","~","bd","~","~","~","bd","~","~","~","bd","~","~","~"],
  ["bd","~","bd","~","~","bd","~","~","bd","~","~","bd","~","~","~","~"],
  ["bd","~","~","bd","bd","~","~","~","~","bd","~","~","bd","~","~","~"],
  ["bd","~","~","~","bd","~","bd","~","~","~","bd","~","bd","~","~","~"]
]

const genKick = (i) => {
  let grid = seedKickGrids[i % seedKickGrids.length].slice() // copy
  for (let s = 0; s < grid.length; s++){
    if (grid[s] === "bd" && rand() < 0.15) grid[s] = "~"
    if (grid[s] === "~"  && rand() < 0.08) grid[s] = "bd"
  }
  return grid.join(' ')
}

// === SNARE: backbeat + ghosts ===
const genSnare = (i) => {
  let g = Array(16).fill('~')
  g[4] = "sd"; g[12] = "sd"
  let ghostProb = 0.05 + (i % 4) * 0.03
  for (let s = 0; s < 16; s++){
    if (g[s] === "~" && rand() < ghostProb) g[s] = (rand() < 0.7 ? "sd" : "~")
  }
  if (i % 3 === 0 && rand() < 0.5) { // flam-ish extra
    let pos = 1 + Math.floor(rand()*14)
    g[pos] = "sd"
  }
  return g.join(' ')
}

// === HATS: evolving density, open/closed mix ===
const genHats = (i) => {
  let density = 0.35 + (i % 8) * 0.03
  let g = Array(16).fill('~')
  for (let s = 0; s < 16; s++){
    if (rand() < density) g[s] = (rand() < 0.8 ? "hh" : "oh")
  }
  return g.join(' ')
}

// === BASS: modal 8-bit line, per-bar variation ===
const bassScale = ['e2','g2','a2','b1','d2','e2','g2','a2']
const genBass = (i) => {
  let g = Array(16).fill('~')
  let idx = (i * 2) % bassScale.length
  ;[0,4,7,10,14].forEach((p,k)=> g[p] = bassScale[(idx+k)%bassScale.length])
  if (i % 4 === 2) g[2] = bassScale[(idx+5)%bassScale.length]
  if (i % 5 === 3) g[8] = bassScale[(idx+3)%bassScale.length]
  return g.join(' ')
}

// =====================================================
// INTRO (4 bars): sparse drums + 8-bit lead + guitar
// =====================================================
let introDrums = stack(
  s("bd ~ bd ~  bd ~ bd ~  bd ~ bd ~  bd ~ bd ~").bank('crate').gain(1),
  s("~ ~ ~ ~  sd ~ ~ ~  ~ ~ ~ ~  sd ~ ~ ~").bank('crate').gain(0.8).late(0.01),
  s("~ hh ~ hh  ~ hh ~ hh  ~ hh ~ hh  ~ hh ~ hh").bank('crate')
    .gain(0.32).mask(swingMask).late(microSwing).hpf(900)
)
let introLead  = lead8(note("e4 ~ g4 ~  b4 ~ ~ ~  d5 ~ b4 ~  g4 ~ ~ ~"))
let introGtr   = gtrStab(s("gtr ~ ~ ~  ~ ~ gtr ~  ~ ~ ~ gtr  ~ ~ gtr ~"))
let intro      = stack(introDrums, introLead, introGtr).segment(4)

// =====================================================
// VERSE (16 bars): evolving drums + bass + pad
// =====================================================
let vKickBars  = Array.from({length:16}, (_,i)=> s(genKick(i)).bank('crate').gain(1.05))
let vSnareBars = Array.from({length:16}, (_,i)=> s(genSnare(i)).bank('crate').gain(0.95).late(0.012))
let vHatBars   = Array.from({length:16}, (_,i)=> s(genHats(i)).bank('crate')
                                  .gain(0.34).mask(swingMask).late(microSwing).hpf(800))
let vDrumBars  = vKickBars.map((_,i)=> stack(vKickBars[i], vSnareBars[i], vHatBars[i]))
let verseDrums = cat(vDrumBars)

let vBassBars  = Array.from({length:16}, (_,i)=> bass8(note(genBass(i))))
let verseBass  = cat(vBassBars)

let versePad   = pad8(
  note("[e3 g3 b3] ~ [d3 g3 b3] ~  [a2 e3 a3] ~ [g2 b2 e3] ~").slow(2)
).gain(0.3)

let verse = stack(verseDrums, verseBass, versePad).size(8)   // 16 bars

// =====================================================
// CHORUS (8 bars): tighter backbeat + hook lead + pads
// =====================================================
let cK  = "bd ~ bd ~  bd ~ bd ~  bd ~ bd ~  bd ~ bd ~"
let cS  = "~ ~ ~ ~  sd ~ ~ ~  ~ ~ ~ ~  sd ~ ~ ~"
let cHH = "hh ~ hh ~  oh ~ hh ~   hh ~ hh ~  oh ~ hh ~"

let chorusDrums = stack(
  s(cK).bank('crate').gain(1.1),
  s(cS).bank('crate').gain(1.0).late(0.015),
  s(cHH).bank('crate').gain(0.42).mask(swingMask).late(microSwing+0.004).hpf(650)
).segment(8)

let chorusBass = bass8(
  note("e2 e2  g2 ~   b1 e2  a2 ~   e2 g2  b1 ~   d2 e2  g2 ~")
).gain(1.0)

let chorusHook = lead8(
  note("e4 g4 b4 e5   d5 b4 g4 e4   g4 a4 b4 d5   b4 a4 g4 e4")
).gain(0.9).crush(5)

let chorusPad = pad8(
  note("[e3 b3] ~ [a3 e4] ~  [g3 d4] ~ [b3 e4] ~").slow(2)
)

let chorus = stack(chorusDrums, chorusBass, chorusHook, chorusPad).size(4) // 8 bars

// =====================================================
// BREAKDOWN (4 bars)
// =====================================================
let breakdown = stack(
  s("bd ~ ~ ~  ~ ~ ~ ~  bd ~ ~ ~  ~ ~ ~ ~").bank('crate').gain(0.75).lpf(500),
  lead8(note("e4 ~ ~ ~  g4 ~ ~ ~  b4 ~ ~ ~  e4 ~ ~ ~")).gain(0.45),
  pad8(note("[e3 g3 b3] ~ ~ ~ [a3 e4] ~ ~ ~")).gain(0.25)
).segment(4)

// =====================================================
// CHORUS RETURN (8 bars) & OUTRO (4 bars)
// =====================================================
let chorus2 = chorus

let outro = stack(
  gtrStab(s("gtr ~ ~ ~  ~ ~ ~ ~  gtr ~ ~ ~  ~ ~ ~ ~")).gain(0.55),
  bass8(note("e2 ~ ~ ~  d2 ~ ~ ~  b1 ~ ~ ~  e2 ~ ~ ~")).gain(0.85),
  s("bd ~ ~ ~  ~ ~ ~ ~  bd ~ ~ ~  ~ ~ ~ ~").bank('crate').gain(0.7)
).segment(4)

// --- full arrangement ---
seq([intro, verse, chorus, breakdown, chorus2, outro])



https://tinyurl.com/ycyu8hca

await samples({'gtr': 'gtr/0001_cleanC.wav'}, 'github:tidalcycles/Dirt-Samples/master/');
samples('github:eddyflux/crate')

setcpm(15/4)

// Melodic instrument definitions
const melodia   = x => x.note().s("ocarina").gain(0.6).clip(1).release(0.1).color(2)
const guitar    = x => x.note().s("gtr").room(1).gain(0.35).clip(1).release(0.3)
const accordi   = x => x.note().s("recorder_bass_sus").gain(0.8).clip(1).release(0.5)
const basso     = x => x.note().s("gm_electric_bass_pick").gain(0.7).clip(1).sustain(0.8)
const synth     = x => x.note().s("square").gain(0.4).lpf(1500).resonance(8)

// Enhanced swing with micro-timing variations
let swingMask = "<0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1>/16"
let microSwing = "<0 0.8 0 1.2 0 0.9 0 1.1>/8"

// Evolving harmonic progression
const scalaIntro = "<c#4:major>"
const scalaVerse = "<c#4:major [f#4:major b3:major] [g#4:minor e4:major] [a4:major f#4:minor]>"
const scalaChorus = "<[e4:major a4:major] [f#4:minor b4:major] [g#4:minor c#5:major] [a4:major d#4:dim]>"
const scalaBreakdown = "<c#4:minor [f#4:minor bb3:major] [g#4:dim e4:minor]>"

// ---------- INTRO (4 bars): Complex beat + melodic entry ----------
let introDrums = cat(
  "bd ~ bd bd  ~ bd ~ ~  bd ~ ~ bd  bd ~ bd ~",
  "bd bd ~ ~  bd ~ bd bd  ~ ~ bd ~  bd ~ ~ bd",
  "~ bd ~ bd  bd ~ ~ ~  bd bd ~ bd  ~ bd ~ ~",
  "bd ~ ~ bd  ~ bd bd ~  bd ~ bd ~  ~ ~ bd bd"
)

let introHats = cat(
  "hh ~ hh hh  ~ oh ~ hh  hh ~ hh ~  oh hh ~ ~",
  "~ hh ~ oh  hh hh ~ ~  oh ~ hh hh  ~ ~ oh hh",
  "hh oh ~ ~  ~ hh hh oh  ~ hh ~ ~  hh ~ oh ~",
  "oh ~ hh ~  hh ~ ~ hh  oh hh ~ hh  ~ oh ~ hh"
)

let introMelody = "<[0,2,4] ~ [4,6] ~ [2,5] ~ [0,4] ~>".scale(scalaIntro).apply(melodia)
let introGuitar = "<4 2 0 ~ 4 ~ 2 0>".scale(scalaIntro).transpose(-12).apply(guitar)

let intro = stack(
  s(introDrums).bank('crate').gain(0.9),
  s(introHats).bank('crate').gain(0.3).late(microSwing).hpf(1000),
  introMelody,
  introGuitar
).segment(4)

// ---------- VERSE (16 bars): Melodic foundation with complex rhythms ----------
let vK = cat(
  "bd ~ ~ ~  bd ~ ~ ~  bd ~ ~ ~  bd ~ ~ ~",
  "bd ~ ~ ~  bd ~ bd ~  bd ~ ~ ~  ~ bd ~ ~",
  "bd ~ bd ~  bd ~ ~ ~  bd ~ ~ bd  ~ ~ ~ ~",
  "bd ~ ~ ~  ~ bd ~ bd  ~ ~ bd ~  bd ~ ~ ~"
)

let vS = cat(
  "~ ~ ~ ~  sd ~ ~ ~  ~ ~ ~ ~  sd ~ ~ ~",
  "~ ~ ~ ~  sd ~ ~ ~  ~ ~ sd ~  ~ sd ~ ~",
  "~ ~ sd ~  ~ ~ ~ ~  sd ~ ~ ~  ~ ~ ~ sd",
  "~ sd ~ ~  sd ~ ~ ~  ~ ~ ~ ~  sd ~ sd ~"
)

let vHH = cat(
  "~ hh ~ hh  ~ hh ~ hh  ~ hh ~ hh  ~ hh ~ hh",
  "hh ~ hh ~  oh ~ hh ~  hh ~ oh ~  hh ~ hh ~",
  "~ oh hh ~  hh ~ ~ hh  oh ~ hh ~  ~ hh oh ~",
  "hh hh ~ ~  ~ oh hh hh  ~ ~ hh oh  hh ~ ~ hh"
)

let verseDrums = stack(
  s(vK).bank('crate').gain(1.0),
  s(vS).bank('crate').gain(0.9),
  s(vHH).bank('crate').gain(0.35).late(microSwing).mask(swingMask).hpf(800)
).segment(16)

// Melodic verse elements
let verseAccordi = "<[0,2,4]*2 [2,4,6] [4,6,1] [0,2,5]>".struct("[x ~]*4").scale(scalaVerse).apply(accordi)
let verseGuitar = "<[4 2 0 4]*2 [6 4 2 6] [1 4 6 1] [2 0 4 2]>".scale(scalaVerse).transpose(-12).apply(guitar)
let verseBasso = "<-3 ~ -1 ~ 0 ~ -3 ~ 2 ~ 0 ~ -1 ~ 4 ~>".scale(scalaVerse).transpose(-12).apply(basso)

// Lead melody line
let verseMelody = cat(
  "0 ~ 2 4 ~ 6 ~ 4 2 ~ 0 ~ 4 2 ~ ~",
  "2 4 ~ 6 4 ~ 2 ~ 0 2 ~ 4 ~ 6 4 2",
  "~ 4 6 ~ 1 4 ~ 6 ~ 2 4 ~ 0 ~ 2 ~",
  "4 ~ 2 ~ 0 4 ~ 2 6 ~ 4 2 ~ 0 ~ 4"
).scale(scalaVerse).transpose(12).apply(melodia)

let verse = stack(verseDrums, verseAccordi, verseGuitar, verseBasso, verseMelody).size(8)

// ---------- CHORUS (16 bars): Full melodic complexity ----------
let cK = cat(
  "bd ~ bd ~  bd ~ bd ~  bd ~ bd ~  bd ~ bd ~",
  "bd bd ~ ~  bd ~ ~ bd  ~ bd bd ~  bd ~ ~ ~",
  "bd ~ ~ bd  bd bd ~ ~  ~ bd ~ bd  bd ~ bd ~",
  "bd ~ bd bd  ~ ~ bd ~  bd ~ ~ ~  bd bd ~ bd"
)

let cS = cat(
  "~ ~ ~ ~  sd ~ ~ ~  ~ ~ ~ ~  sd ~ ~ ~",
  "~ ~ ~ sd  ~ ~ ~ ~  sd ~ sd ~  ~ ~ ~ ~",
  "~ ~ ~ ~  sd sd ~ ~  ~ ~ ~ sd  sd ~ ~ ~",
  "~ sd ~ ~  ~ ~ sd ~  ~ sd ~ ~  sd ~ sd ~"
)

let cHH = cat(
  "hh ~ hh ~  oh ~ hh ~   hh ~ hh ~  oh ~ hh ~",
  "~ hh oh hh  ~ hh ~ oh  hh ~ ~ hh  oh hh ~ ~",
  "hh oh ~ ~  hh ~ hh oh  ~ hh hh ~  ~ oh hh hh",
  "oh ~ hh hh  ~ oh ~ ~  hh hh oh ~  hh ~ ~ oh"
)

let chorusDrums = stack(
  s(cK).bank('crate').gain(1.1),
  s(cS).bank('crate').gain(1.0),
  s(cHH).bank('crate').gain(0.4).late(microSwing).mask(swingMask).hpf(600)
).segment(16)

// Rich harmonic chorus
let chorusAccordi = "<[0,2,4,6]*4 [2,4,6,1] [4,6,1,3] [0,2,5,7]>".struct("[x ~ x ~]*2").scale(scalaChorus).apply(accordi)

let chorusGuitar1 = "<[0 4 2 6]*2 [4 1 6 3] [2 6 4 1] [0 5 2 7]>".scale(scalaChorus).transpose(-12).apply(guitar)
let chorusGuitar2 = "<[4 0 6 2] [1 4 3 6] [6 2 1 4] [5 0 7 2]>".scale(scalaChorus).transpose(-24).apply(guitar)

let chorusBasso = "<0 ~ 2 ~ 4 ~ 0 ~ 1 ~ 4 ~ 2 ~ 6 ~>".scale(scalaChorus).transpose(-24).apply(basso)

// Soaring lead melodies
let chorusMelody1 = cat(
  "0 2 4 6 ~ 4 2 ~ 6 4 ~ 2 0 ~ 4 ~",
  "2 ~ 4 6 1 ~ 4 ~ 6 1 4 ~ 2 6 ~ 4",
  "4 6 ~ 1 3 6 ~ 4 1 ~ 3 ~ 6 4 1 ~",
  "0 ~ 2 5 ~ 7 2 ~ 5 0 ~ 7 ~ 2 5 0"
).scale(scalaChorus).transpose(12).apply(melodia)

let chorusMelody2 = cat(
  "~ 4 ~ 6 4 ~ 2 4 ~ 6 4 2 ~ 0 ~ 2",
  "4 2 ~ 6 ~ 1 4 6 ~ 3 1 ~ 4 ~ 2 ~",
  "6 ~ 1 ~ 4 3 ~ 6 4 1 ~ 3 ~ 6 ~ 4",
  "2 5 ~ 0 7 ~ 5 2 0 ~ 7 ~ 5 ~ 2 7"
).scale(scalaChorus).transpose(24).apply(melodia).gain(0.4)

let chorusSynth = "<[0,4,6] ~ [2,6,1] ~ [4,1,3] ~ [0,5,7] ~>".scale(scalaChorus).apply(synth)

let chorus = stack(
  chorusDrums, 
  chorusAccordi, 
  chorusGuitar1, 
  chorusGuitar2, 
  chorusBasso, 
  chorusMelody1, 
  chorusMelody2,
  chorusSynth
).size(8)

// ---------- BREAKDOWN (8 bars): Filtered melodic focus ----------
let breakdownDrums = s("bd ~ ~ ~  ~ ~ ~ ~  bd ~ ~ ~  ~ ~ ~ ~")
  .bank('crate')
  .gain(0.6)
  .lpf(sine.slow(2).range(200, 800))
  .segment(8)

let breakdownMelody = "<0 ~ ~ ~ 2 ~ ~ ~ 4 ~ ~ ~ 0 ~ ~ ~>".scale(scalaBreakdown).transpose(12).apply(melodia)
let breakdownGuitar = "<-3 ~ ~ ~ 0 ~ ~ ~ 2 ~ ~ ~ -1 ~ ~ ~>".scale(scalaBreakdown).apply(guitar)
let breakdownBasso = "<-7 ~ ~ ~ -5 ~ ~ ~ -3 ~ ~ ~ -7 ~ ~ ~>".scale(scalaBreakdown).apply(basso)

let breakdown = stack(
  breakdownDrums, 
  breakdownMelody, 
  breakdownGuitar, 
  breakdownBasso
).room(0.8).segment(8)

// ---------- OUTRO (4 bars): Melodic resolution ----------
let outroMelody = "<0 2 4 6 4 2 0 ~ ~ ~ ~ ~ ~ ~ ~ ~>".scale(scalaIntro).transpose(12).apply(melodia)
let outroGuitar = "<-3 -1 0 2 0 -1 -3 ~ ~ ~ ~ ~ ~ ~ ~ ~>".scale(scalaIntro).apply(guitar)
let outroDrums = s("bd ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~").bank('crate').gain(0.7).segment(4)

let outro = stack(outroMelody, outroGuitar, outroDrums)

// ---------- Final arrangement ----------
seq([intro, verse, chorus, breakdown, chorus.fast(1.5), outro])


https://tinyurl.com/2xaxupbj

// --- samples ---
await samples({ gtr: 'gtr/0001_cleanC.wav' }, 'github:tidalcycles/Dirt-Samples/master/')
samples('github:eddyflux/crate')

// --- tempo ---
setcpm(25/4)                       // slow, funky
let swingMask  = "<0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1>/16"
let microSwing = 0.02              // subtle shove on offbeats

// --- simple sound wrappers (only Strudel-safe ops) ---
const lead8   = p => p.s("square").gain(0.65).crush(6).lpf(1600).delay(0.25).delayfb(0.25)
const bass8   = p => p.s("square").gain(0.95).lpf(750)
const pad8    = p => p.s("sawtooth").gain(0.35).lpf(1200).room(0.8).size(2)
const gtrStab = p => p.s("gtr").gain(0.45).room(0.6)
const triumphPad = p => p.s("sawtooth").gain(0.5).lpf(800).room(0.9).size(4)

const rand = Math.random

// === KICK: seeded arrays (no .split) ===
const seedKickGrids = [
  ["bd","~","~","~","bd","~","~","~","bd","~","~","~","bd","~","~","~"],
  ["bd","~","bd","~","~","bd","~","~","bd","~","~","bd","~","~","~","~"],
  ["bd","~","~","bd","bd","~","~","~","~","bd","~","~","bd","~","~","~"],
  ["bd","~","~","~","bd","~","bd","~","~","~","bd","~","bd","~","~","~"]
]

const genKick = (i) => {
  let grid = seedKickGrids[i % seedKickGrids.length].slice() // copy
  for (let s = 0; s < grid.length; s++){
    if (grid[s] === "bd" && rand() < 0.15) grid[s] = "~"
    if (grid[s] === "~"  && rand() < 0.08) grid[s] = "bd"
  }
  return grid.join(' ')
}

// === SNARE: backbeat + ghosts ===
const genSnare = (i) => {
  let g = Array(16).fill('~')
  g[4] = "sd"; g[12] = "sd"
  let ghostProb = 0.05 + (i % 4) * 0.03
  for (let s = 0; s < 16; s++){
    if (g[s] === "~" && rand() < ghostProb) g[s] = (rand() < 0.7 ? "sd" : "~")
  }
  if (i % 3 === 0 && rand() < 0.5) { // flam-ish extra
    let pos = 1 + Math.floor(rand()*14)
    g[pos] = "sd"
  }
  return g.join(' ')
}

// === HATS: evolving density, open/closed mix ===
const genHats = (i) => {
  let density = 0.35 + (i % 8) * 0.03
  let g = Array(16).fill('~')
  for (let s = 0; s < 16; s++){
    if (rand() < density) g[s] = (rand() < 0.8 ? "hh" : "oh")
  }
  return g.join(' ')
}

// === BASS: modal 8-bit line, per-bar variation ===
const bassScale = ['e2','g2','a2','b1','d2','e2','g2','a2']
const genBass = (i) => {
  let g = Array(16).fill('~')
  let idx = (i * 2) % bassScale.length
  ;[0,4,7,10,14].forEach((p,k)=> g[p] = bassScale[(idx+k)%bassScale.length])
  if (i % 4 === 2) g[2] = bassScale[(idx+5)%bassScale.length]
  if (i % 5 === 3) g[8] = bassScale[(idx+3)%bassScale.length]
  return g.join(' ')
}

// =====================================================
// INTRO (4 bars): sparse drums + 8-bit lead + guitar
// =====================================================
let introDrums = stack(
  s("bd ~ bd ~  bd ~ bd ~  bd ~ bd ~  bd ~ bd ~").bank('crate').gain(1),
  s("~ ~ ~ ~  sd ~ ~ ~  ~ ~ ~ ~  sd ~ ~ ~").bank('crate').gain(0.8).late(0.01),
  s("~ hh ~ hh  ~ hh ~ hh  ~ hh ~ hh  ~ hh ~ hh").bank('crate')
    .gain(0.32).mask(swingMask).late(microSwing).hpf(900)
)
let introLead  = lead8(note("e4 ~ g4 ~  b4 ~ ~ ~  d5 ~ b4 ~  g4 ~ ~ ~"))
let introGtr   = gtrStab(s("gtr ~ ~ ~  ~ ~ gtr ~  ~ ~ ~ gtr  ~ ~ gtr ~"))
let intro      = stack(introDrums, introLead, introGtr).segment(4)

// =====================================================
// VERSE (16 bars): evolving drums + bass + pad
// =====================================================
let vKickBars  = Array.from({length:16}, (_,i)=> s(genKick(i)).bank('crate').gain(1.05))
let vSnareBars = Array.from({length:16}, (_,i)=> s(genSnare(i)).bank('crate').gain(0.95).late(0.012))
let vHatBars   = Array.from({length:16}, (_,i)=> s(genHats(i)).bank('crate')
                                  .gain(0.34).mask(swingMask).late(microSwing).hpf(800))
let vDrumBars  = vKickBars.map((_,i)=> stack(vKickBars[i], vSnareBars[i], vHatBars[i]))
let verseDrums = cat(vDrumBars)

let vBassBars  = Array.from({length:16}, (_,i)=> bass8(note(genBass(i))))
let verseBass  = cat(vBassBars)

let versePad   = pad8(
  note("[e3 g3 b3] ~ [d3 g3 b3] ~  [a2 e3 a3] ~ [g2 b2 e3] ~").slow(2)
).gain(0.3)

// Add a subtle build-up pad to the verse
let buildUpPad = pad8(note("[e3 g3 b3] [d3 g3 b3] [a2 e3 a3] [g2 b2 e3]"))
  .slow(4).gain(0.1).lpf(400).room(0.7)

let verse = stack(verseDrums, verseBass, versePad, buildUpPad).size(8)   // 16 bars

// =====================================================
// CHORUS (8 bars): tighter backbeat + hook lead + pads
// =====================================================
let cK  = "bd ~ bd ~  bd ~ bd ~  bd ~ bd ~  bd ~ bd ~"
let cS  = "~ ~ ~ ~  sd ~ ~ ~  ~ ~ ~ ~  sd ~ ~ ~"
let cHH = "hh ~ hh ~  oh ~ hh ~   hh ~ hh ~  oh ~ hh ~"

let chorusDrums = stack(
  s(cK).bank('crate').gain(1.1),
  s(cS).bank('crate').gain(1.0).late(0.015),
  s(cHH).bank('crate').gain(0.42).mask(swingMask).late(microSwing+0.004).hpf(650)
).segment(8)

let chorusBass = bass8(
  note("e2 e2  g2 ~   b1 e2  a2 ~   e2 g2  b1 ~   d2 e2  g2 ~")
).gain(1.0)

let chorusHook = lead8(
  note("e4 g4 b4 e5   d5 b4 g4 e4   g4 a4 b4 d5   b4 a4 g4 e4")
).gain(0.9).crush(5)

let chorusPad = pad8(
  note("[e3 b3] ~ [a3 e4] ~  [g3 d4] ~ [b3 e4] ~").slow(2)
)

// Add a higher pad for the chorus
let chorusHighPad = triumphPad(note("[e3 b3 e4] [a3 e4 a4] [g3 d4 g4] [b3 e4 b4]")).slow(2)

let chorus = stack(chorusDrums, chorusBass, chorusHook, chorusPad, chorusHighPad).size(4) // 8 bars

// =====================================================
// TRANSITION (4 bars): builds to chorus
// =====================================================
let transition = stack(
  s("bd ~ ~ ~  ~ ~ ~ ~  bd ~ ~ ~  ~ ~ ~ ~").bank('crate').gain(0.8),
  bass8(note("e2 ~ ~ ~  d2 ~ ~ ~  b1 ~ ~ ~  e2 ~ ~ ~")),
  buildUpPad.gain(0.35).lpf(800).room(0.8) // rising pad
).segment(4)

// =====================================================
// BREAKDOWN (4 bars)
// =====================================================
let breakdown = stack(
  s("bd ~ ~ ~  ~ ~ ~ ~  bd ~ ~ ~  ~ ~ ~ ~").bank('crate').gain(0.75).lpf(500),
  lead8(note("e4 ~ ~ ~  g4 ~ ~ ~  b4 ~ ~ ~  e4 ~ ~ ~")).gain(0.45),
  pad8(note("[e3 g3 b3] ~ ~ ~ [a3 e4] ~ ~ ~")).gain(0.25)
).segment(4)

// =====================================================
// CHORUS RETURN (8 bars) & OUTRO (4 bars)
// =====================================================
let chorus2 = chorus

let outro = stack(
  gtrStab(s("gtr ~ ~ ~  ~ ~ ~ ~  gtr ~ ~ ~  ~ ~ ~ ~")).gain(0.55),
  bass8(note("e2 ~ ~ ~  d2 ~ ~ ~  b1 ~ ~ ~  e2 ~ ~ ~")).gain(0.85),
  s("bd ~ ~ ~  ~ ~ ~ ~  bd ~ ~ ~  ~ ~ ~ ~").bank('crate').gain(0.7),
  triumphPad(note("[e3 g3 b3 e4]")).slow(4) // sustained triumphant chord
).segment(8) // extended outro

// --- full arrangement ---
seq([intro, verse, transition, chorus, breakdown, chorus2, outro])