Skip to content

Meta Events

Meta events are plain objects you yield from the song generator alongside patterns. The engine intercepts them at the cycle boundary and applies their effect.

function* song(ctx) {
yield ctl.tempo(140)
yield ctl.seed(42, { once: true })
yield cast(synth, key, seq(4, '0,2,4,~'), ctx)
}

Helper functions

Two helper namespaces build these events for you — they are to transport/FX what cast() is to sound and v.* is to visuals. Each returns a plain event object, so yielding raw objects (the “raw form” shown throughout this page) remains equally valid.

  • ctl.* — transport / control: ctl.tempo(bpm, opts?), ctl.bpc(n, opts?), ctl.seed(n, opts?), ctl.eos(beat?).
  • fx.* — master FX (distinct from per-machine insert FX like reverb()): fx.reverb(mix, opts?), fx.delay(time, feedback, opts?), fx.gain(g, opts?).
yield ctl.tempo(120, { once: true })
yield ctl.bpc(8)
yield ctl.seed(42, { once: true })
yield ctl.eos(4)
yield fx.reverb(0.3)
yield fx.delay(0.375, 0.4, { beat: 2 })
yield fx.gain(0.8)

Common fields

FieldTypeDescription
typestringIdentifies the event kind (required)
oncebooleanWhen true, the event applies only on the first cycle after play() or swap(). Subsequent cycles ignore it.
beatnumberOffset in beats within the cycle at which the event fires (FX events only, default 0).

tempo — Change BPM

yield ctl.tempo(160)
yield ctl.tempo(90, { once: true }) // set once at start
// raw form (equivalent)
yield { type: 'tempo', bpm: 90, once: true }

Takes effect at the next cycle boundary, so all notes in the current cycle still use the previous BPM.

FieldTypeDescription
bpmnumberNew beats-per-minute value (must be positive)

bpc — Change beats per cycle

yield ctl.bpc(8)
// raw form (equivalent)
yield { type: 'bpc', bpc: 8 }

Changes how many beats make up one cycle.

FieldTypeDescription
bpcnumberNew cycle length in beats (positive integer)

seed — Set random seed

yield ctl.seed(42, { once: true })
// raw form (equivalent)
yield { type: 'seed', seed: 42, once: true }

Sets the random seed used to generate ctx.rand() values. With the same seed, the same sequence of ctx.rand() calls produces the same values — play → stop → play always sounds identical.

The seed takes effect starting from the next cycle. Use once: true so the seed is re-established after each swap() without overriding changes made later in the song.

FieldTypeDescription
seednumberInteger seed value. Any finite number is accepted.

Reproducibility guarantee

When you call play() or swap():

  1. The seed resets to the value passed to new Incanto({ seed }) (default 0).
  2. On the first cycle, ctx.rand() is derived from that initial seed.
  3. When a seed event is processed, subsequent cycles use the new seed with a per-generator cycle counter that restarts from 0 on each play() / swap().

This means two songs with the same seed event and the same generator always produce the same ctx.rand() sequence from cycle 1 onwards.

Example — reproducible variation

function* song(ctx) {
yield ctl.seed(1337, { once: true })
const key = scales(4, 'A3:minor')
const synth = vasynth({ wave: 'sawtooth', gain: 0.5 })
while (true) {
// ctx.rand() returns a deterministic value in [0, 1)
const octave = ctx.rand() > 0.5 ? 1 : 0
yield cast(synth, key, seq(4, `${octave},2,4,~`), ctx)
}
}

Random numbers

ctx.rand is the seeded generator. Call it for a float, or use .int for an integer — both draw from the same reproducible stream.

ctx.rand() // float in [0, 1)
ctx.rand(8) // float in [0, 8)
ctx.rand(0.2, 0.8) // float in [0.2, 0.8)
ctx.rand.int(0, 3) // integer in [0, 3] — both ends inclusive
ctx.rand.int(1, 6) // dice roll: 1..6

Handy for randomizing song development deterministically — e.g. pick a section or a transposition each cycle:

function* song(ctx) {
yield ctl.seed(99, { once: true })
const keys = ['C4:minor', 'F4:dorian', 'A3:phrygian']
const key = scales(4, keys[ctx.rand.int(0, keys.length - 1)])
const cutoff = ctx.rand(400, 3000)
yield cast(vasynth({ cutoff }), key, seq(4, '0,2,4,~'), ctx)
}
CallReturns
ctx.rand()float [0, 1)
ctx.rand(max)float [0, max)
ctx.rand(min, max)float [min, max)
ctx.rand.int(min, max)integer [min, max] (inclusive)

eos — Stop playback

yield ctl.eos() // stop at end of the cycle
yield ctl.eos(4) // stop at beat 4
// raw form (equivalent)
yield { type: 'eos', beat: 4 }

Stops the transport. Without beat, playback ends at the cycle’s last beat.

FieldTypeDescription
beatnumberCycle beat offset at which to stop (default = end of cycle)

fx_reverb — Master reverb mix

yield fx.reverb(0.3)
yield fx.reverb(0.6, { beat: 2 }) // fade in at beat 2
// raw form (equivalent)
yield { type: 'fx_reverb', mix: 0.6, beat: 2 }
FieldTypeDescription
mixnumberWet/dry ratio: 0 = dry, 1 = full wet
beatnumberCycle beat offset for the change (default 0)

fx_delay — Ping-pong delay

yield fx.delay(0.375, 0.4)
// raw form (equivalent)
yield { type: 'fx_delay', time: 0.375, feedback: 0.4 }
FieldTypeDescription
timenumberDelay time in seconds
feedbacknumberFeedback amount: 0 = no repeats, <1 = decaying repeats
beatnumberCycle beat offset (default 0)

fx_gain — Master output gain

yield fx.gain(0.8)
// raw form (equivalent)
yield { type: 'fx_gain', gain: 0.8 }
FieldTypeDescription
gainnumberOutput gain multiplier (1.0 = unity gain)
beatnumberCycle beat offset (default 0)

swap — Switch active deck (Dual Deck mode)

When the editor is in Dual Deck mode, yielding a swap event at a specific cycle transitions playback to the other deck at the next cycle boundary.

function* song(ctx) {
// switch to deck B after 4 cycles
if (ctx.cycle === 4) yield { type: 'swap', target: 'B' }
yield cast(synth, key, seq(4, '0,2,4,~'), ctx)
}
FieldTypeDescription
target'A' | 'B'Deck to switch to. Omit to swap to the opposite deck.

The swap takes effect at the next cycle boundary (same as Mod-Enter Apply). If the editor is in Single Deck mode or the target deck has no code, the event is ignored.


User-defined events

Any object with a type string and no machine key is treated as a meta event and passed to the onMeta callback you provide when constructing Incanto. This lets you synchronise lyrics, chord labels, lighting cues, or any external state with the music.

yield { type: 'lyrics', text: 'verse 1', beat: 0 }
yield { type: 'section', name: 'chorus' }

The engine delivers user-defined events via setTimeout at the correct audio time so they stay in sync with the notes.