3. Melody & Harmony
Real music has multiple simultaneous parts. In Nimbus, each yield in a cycle plays in parallel — just add more lines.
Multiple voices
function* song(ctx) { const lead = vasynth({ wave: 'triangle', cutoff: 3000, release: 0.3 }) const pad = vasynth({ wave: 'sawtooth', cutoff: 600, attack: 0.5, release: 1.5, gain: 0.3 }) const key = scales(4, 'D4:minor')
yield cast(lead, key, seq(4, '0,2,4,3,2,0,_,_'), ctx) yield cast(pad, key, seq(4, '{0,2,4},~,~,~'), ctx)}Each cast is an independent voice. They share the same scale, so their notes always fit together.
chords — chord progressions
chords(beats, scale, progression) cycles through Roman-numeral chords, with each chord changing the note pool for every cast that uses it.
const key = scales(4, 'C4:major')const prog = chords(8, key, 'I,V,VI,IV') // classic I–V–vi–IVPass prog to cast instead of key:
yield cast(lead, prog, seq(4, '0,2,4,2'), ctx)Now the melody automatically fits the chord changes.
Common progressions
chords(8, key, 'I,V,VI,IV') // pop (C G Am F)chords(8, key, 'I,IV,V,I') // blues / gospelchords(8, key, 'II,V,I,I') // jazz II–V–Ichords(4, key, 'I,VII,VI,VII') // rock (I–bVII–bVI–bVII)chords(8, key, 'Imaj7,IVmaj7,IIm7,V7') // smooth jazzChord quality suffixes
You can override the diatonic quality:
chords(8, key, 'Imaj7,IVmaj7,V7,I') // jazz major soundchords(8, key, 'Im7,IVm7,bVIImaj7,Im7') // minor funkchords(4, key, 'Idim7,IIdim7,IIIdim7') // fully diminished — tense!Holding chords longer
Use ~ to extend a chord across extra beats:
chords(8, key, 'I~~,V~~,VI,IV') // I for 3 beats, V for 3 beats, then VI and IV one beat eachMelody over chords
The key insight: when you cast a melody against a ChordPattern, every scale degree resolves against the current chord — not against the root key. Degree 0 is always the chord root, 2 is the chord third, 4 is the fifth.
function* song(ctx) { const lead = vasynth({ wave: 'triangle', cutoff: 4000, release: 0.35, gain: 0.55 }) const chd = vasynth({ wave: 'sawtooth', cutoff: 500, attack: 0.4, release: 1.2, gain: 0.25 }) const bass = vasynth({ wave: 'square', cutoff: 400, attack: 0.01, release: 0.2, gain: 0.6 })
const key = scales(4, 'D4:minor') const bKey = key.transpose(-24) // two octaves lower for bass const prog = chords(8, key, 'I,VII,VI,VII')
yield cast(lead, prog, seq(4, '0,2,4,2,3,2,0,_'), ctx) yield cast(chd, prog, seq(8, '{0,2,4},~,~,~,{0,2,4},~,~,~'), ctx) yield cast(bass, prog, seq(4, '0,_,_,_,0,_,4,_'), ctx)}Bass lines
A bass line typically plays the chord root two octaves below. Use .transpose() on the chord pattern or scale:
const bProg = prog.transpose(-24) // two octaves downyield cast(bass, bProg, seq(4, '0,_,0,4,_,0,_,_'), ctx)Walking bass
Move between chord tones and passing tones:
// Arpeggiate the chord root–third–fifth–thirdyield cast(bass, prog, seq(4, '0,2,4,2'), ctx)
// Simpler two-note patternyield cast(bass, prog, seq(4, '0,_,4,_'), ctx)
// Funky: syncopated with a pushyield cast(bass, prog, seq(4, '0,_,_,4,0,_,2,_'), ctx)Octave displacement
Add 7 to a degree to jump an octave up (for 7-note scales):
// Jump to the upper octave on the last noteyield cast(lead, prog, seq(4, '0,2,4,7'), ctx)
// Wide-range arpeggioyield cast(lead, prog, seq(4, '0,2,4,7,4,2,0,_'), ctx)Negative degrees go below the root: -7 = one octave down.
Complete song: melodic pop beat
function* song(ctx) { const lead = vasynth({ wave: 'triangle', cutoff: 3500, attack: 0.005, release: 0.3, gain: 0.55 }) const pad = vasynth({ wave: 'sawtooth', cutoff: 700, attack: 0.6, release: 1.5, gain: 0.2 }) const bass = vasynth({ wave: 'square', cutoff: 350, attack: 0.01, release: 0.15, gain: 0.65 }) const kk = kick() const sn = snare() const hh = hat({ decay: 0.04 })
const key = scales(4, 'F4:minor') const prog = chords(8, key, 'I,VI,III,VII') const bProg = prog.transpose(-24)
yield cast(lead, prog, seq(4, '0,2,4,3,2,4,0,_'), ctx) yield cast(pad, prog, seq(8, '{0,2,4},~,~,~,{0,2,4},~,~,~'), ctx) yield cast(bass, bProg, seq(4, '0,_,_,4,0,_,2,_'), ctx) yield cast(kk, 'drums', seq(4, 'x...x.x.....x...'), ctx) yield cast(sn, 'drums', seq(4, '....x.......x...'), ctx) yield cast(hh, 'drums', seq(4, 'x.x.x.x.x.x.x.x.'), ctx)}Next
4. Sound Design → — shape timbre with synth parameters, FX, and LFO.