1. First Sound
Every Nimbus song is a JavaScript generator function named song. The engine calls it once per cycle and collects everything you yield.
Minimal example
Paste this and press ▶:
function* song(ctx) { const synth = vasynth() const key = scales(4, 'C4:major') yield cast(synth, key, seq(4, '0,2,4,2'), ctx)}You should hear four notes — C, E, G, E — repeating every cycle. Now let’s understand each piece.
vasynth — virtual analog synth
vasynth(options?) creates a synthesizer. With no arguments you get a sawtooth wave with default settings.
Wave shapes
The wave parameter is the most audible difference. Try each one:
// Bright and buzzy — good for leads and bassesconst synth = vasynth({ wave: 'sawtooth' })
// Hollow and dark — good for pads and sub bassesconst synth = vasynth({ wave: 'square' })
// Soft and round — good for flutes and bellsconst synth = vasynth({ wave: 'triangle' })
// Subtle and pure — good for soft backgroundsconst synth = vasynth({ wave: 'sine' })Filter cutoff
cutoff (Hz, 20–20000) controls a low-pass filter — lower values make the sound darker:
// Bright (filter wide open)const synth = vasynth({ wave: 'sawtooth', cutoff: 8000 })
// Dark and muffledconst synth = vasynth({ wave: 'sawtooth', cutoff: 400 })
// Resonant — adds a ringing tone at the cutoff frequencyconst synth = vasynth({ wave: 'sawtooth', cutoff: 1200, resonance: 8 })Amplitude envelope (ADSR)
The envelope shapes how the volume changes over time. attack and release are the most useful to adjust:
// Pluck — instant attack, fast releaseconst synth = vasynth({ attack: 0.001, decay: 0.1, sustain: 0, release: 0.1 })
// Pad — slow fade-in and fade-outconst synth = vasynth({ attack: 0.8, decay: 0.2, sustain: 0.9, release: 1.5 })
// Organ — instant on/offconst synth = vasynth({ attack: 0.001, decay: 0, sustain: 1, release: 0.01 })| Parameter | Default | Range | Effect |
|---|---|---|---|
attack | 0.01 s | 0–10 | Fade-in time from silence to peak |
decay | 0.1 s | 0–10 | Fall from peak to sustain level |
sustain | 0.7 | 0–1 | Volume while note is held |
release | 0.3 s | 0–10 | Fade-out after note ends |
gain | 0.5 | 0–1 | Overall output volume |
seq — the sequence
seq(beats, string) describes the rhythm and melody. The first argument is the length in beats; the second is the note pattern.
Melody mode
Notes are scale degrees separated by commas. 0 = root of the scale, 1 = second note, 7 = one octave up from root.
seq(4, '0,2,4,2') // C E G E — four equal beatsseq(4, '0,~,4,_') // C (held 2 beats), G, restseq(4, '0,_,_,4,_,_,5,_') // sparse 8th-note lineseq(4, '{0,2,4},~,~,~') // Chord: CEG held for 4 beatsseq(4, '[0,2,4],0,_,0') // Triplet on beat 1, then two quarter notes| Token | Meaning |
|---|---|
0–7 (or negative) | Scale degree |
~ | Tie — extends the previous note |
_ | Rest |
! | Repeat — same note, new articulation |
{0,2,4} | Chord — all degrees at once |
[0,2,4] | Subdivision — three events in one beat |
Different lengths
The beats value changes how the pattern fits in the cycle. These all loop at different speeds:
seq(4, '0,2,4,2') // 4-beat pattern, one loop per cycleseq(2, '0,4') // 2-beat pattern, loops twice per cycleseq(8, '0,_,2,_,4,_,3,_') // 8-beat, crosses cycle boundaryscales — the pitch source
scales(beats, string) defines which notes are available. The string format is "ROOT[OCTAVE]:SCALE_NAME".
Common scales
scales(4, 'C4:major') // C major — bright and stablescales(4, 'A3:minor') // A natural minor — dark, melancholyscales(4, 'D4:dorian') // D Dorian — jazzy minorscales(4, 'E4:minor pentatonic') // E minor pentatonic — rock/blues feelscales(4, 'C4:whole tone') // Dreamy, no tonic pullscales(4, 'D4:diminished') // Tense, symmetricalModulating between scales
Pass multiple scales separated by commas — the pattern cycles through them:
// 8-beat pattern: 4 beats in C major, then 4 in G mixolydianscales(8, 'C4:major, G4:mixolydian')
// Chord-change feel with just two scalesscales(4, 'D4:minor, F4:major')Putting it together
Here is a complete 8-bar melody with a pluck synth:
function* song(ctx) { const synth = vasynth({ wave: 'triangle', cutoff: 3000, attack: 0.002, release: 0.4, gain: 0.6 }) const key = scales(8, 'C4:minor, Eb4:major')
yield cast(synth, key, seq(4, '0,2,4,3,2,0,_,_'), ctx)}Try these variations to hear the difference:
// Punchier: shorter releaseconst synth = vasynth({ wave: 'sawtooth', cutoff: 2000, release: 0.1, gain: 0.5 })
// Warmer: lower cutoff with resonanceconst synth = vasynth({ wave: 'square', cutoff: 800, resonance: 3, release: 0.6, gain: 0.5 })
// Pad version: slow attackconst synth = vasynth({ wave: 'triangle', attack: 0.6, sustain: 0.8, release: 1.2, gain: 0.4 })Next
2. Rhythm Section → — add kick, snare, and hi-hat to your song.