Skip to content

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 basses
const synth = vasynth({ wave: 'sawtooth' })
// Hollow and dark — good for pads and sub basses
const synth = vasynth({ wave: 'square' })
// Soft and round — good for flutes and bells
const synth = vasynth({ wave: 'triangle' })
// Subtle and pure — good for soft backgrounds
const 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 muffled
const synth = vasynth({ wave: 'sawtooth', cutoff: 400 })
// Resonant — adds a ringing tone at the cutoff frequency
const 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 release
const synth = vasynth({ attack: 0.001, decay: 0.1, sustain: 0, release: 0.1 })
// Pad — slow fade-in and fade-out
const synth = vasynth({ attack: 0.8, decay: 0.2, sustain: 0.9, release: 1.5 })
// Organ — instant on/off
const synth = vasynth({ attack: 0.001, decay: 0, sustain: 1, release: 0.01 })
ParameterDefaultRangeEffect
attack0.01 s0–10Fade-in time from silence to peak
decay0.1 s0–10Fall from peak to sustain level
sustain0.70–1Volume while note is held
release0.3 s0–10Fade-out after note ends
gain0.50–1Overall 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 beats
seq(4, '0,~,4,_') // C (held 2 beats), G, rest
seq(4, '0,_,_,4,_,_,5,_') // sparse 8th-note line
seq(4, '{0,2,4},~,~,~') // Chord: CEG held for 4 beats
seq(4, '[0,2,4],0,_,0') // Triplet on beat 1, then two quarter notes
TokenMeaning
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 cycle
seq(2, '0,4') // 2-beat pattern, loops twice per cycle
seq(8, '0,_,2,_,4,_,3,_') // 8-beat, crosses cycle boundary

scales — 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 stable
scales(4, 'A3:minor') // A natural minor — dark, melancholy
scales(4, 'D4:dorian') // D Dorian — jazzy minor
scales(4, 'E4:minor pentatonic') // E minor pentatonic — rock/blues feel
scales(4, 'C4:whole tone') // Dreamy, no tonic pull
scales(4, 'D4:diminished') // Tense, symmetrical

Modulating 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 mixolydian
scales(8, 'C4:major, G4:mixolydian')
// Chord-change feel with just two scales
scales(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 release
const synth = vasynth({ wave: 'sawtooth', cutoff: 2000, release: 0.1, gain: 0.5 })
// Warmer: lower cutoff with resonance
const synth = vasynth({ wave: 'square', cutoff: 800, resonance: 3, release: 0.6, gain: 0.5 })
// Pad version: slow attack
const 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.