Early Preview · Kiwisonic, Kiwisynth, Kiwipulse and Kiwibass are free during early preview. Their shape is still being figured out, and feedback is what drives them forward.

Scripting generated tracks

All docs / Kiwisonic / Scripting generated tracks

Kiwisonic manual

Scripting generated tracks

A generated track writes its own part by following another track. A short script decides how: for every note the master plays, or every bar of the clip, you say what notes to place. This page is the full reference, covering the handlers, the values you can read, and every function the language knows.

You do not need to have programmed before. Most scripts are two or three lines, and the shipped examples are good starting points. Open one and change a number.

How a script runs

A script runs at generation time, once whenever the master changes or you press Apply, and it runs per clip. It sweeps the clip and emits notes, and those notes are then real, saved notes that play and export like anything else. The script never runs during playback, so there is no live timing to worry about.

A script is made of handlers, blocks that fire at different moments.

Handler Fires
`on init` once, before everything else. Set up values here.
`on bar` once for every bar in the clip
`on beat` once for every beat in the clip
`on source_note` once for every note the master plays, in time order
`on source_chord` once for every chord played in the master
`on harmonic_chord` once for every chord in the song's progression

A script can have any of these, in any order, and Apply always runs the whole script. Most parts only need on source_note.

# A soft shimmer one octave above each note the master plays.
on source_note
    emit_note(source_pitch + 12, source_beat, source_length, source_velocity * 0.5)
end

The handler runs for every note the master plays. source_pitch is that note's pitch, so adding twelve puts it an octave above, and emit_note places it at the same spot for the same length at half the velocity, which keeps it behind the master.

Placing notes

emit_note(pitch, beat, length, velocity)

pitch is a MIDI note number, where 60 is middle C and 12 is an octave. Anything outside the MIDI range is skipped.

beat is where the note starts, counted as described below. Beats start at 1.

length is optional, in beats, and defaults to one beat.

velocity is optional, from 0 to 1, and defaults to 0.8.

A note starting past the end of the clip is skipped, and one running past the end is trimmed to fit. Call emit_note as many times as you like in one handler. Twice stacks an octave:

on source_note
    emit_note(source_pitch + 12, source_beat, source_length, source_velocity * 0.5)
    emit_note(source_pitch + 24, source_beat, source_length, source_velocity * 0.35)
end

Counting beats

There are four beats to the bar and beat 1 is the downbeat. Where beat 1 lands depends on the handler.

In on source_note, source_beat is measured from the start of the clip, so passing it straight to emit_note lines the note up with the master.

In on bar and on beat, the beat you give emit_note is measured from the start of the current bar. So this lays a note on that bar's downbeat, four beats long:

# A held root under every bar.
on bar
    emit_note(48, 1, 4)
end

In on init, beats are measured from the start of the clip.

For a steady pulse inside the bar, loop inside on bar with a running beat counter. That reads more clearly than on beat, which always positions from the bar's downbeat.

The values you can read

Available anywhere:

Value Meaning
`bar_index` which bar is running, starting at 1
`beat_index` which beat is running, starting at 1
`bar_count` how many bars the clip has
`source_density` how busy the master is, 0 for empty, 1 for a note every beat
`source_low` the lowest pitch the master plays
`source_high` the highest pitch the master plays
`mix_density` how busy the rest of the mix is, on the same scale
`mix_low` the lowest pitch any other track plays
`mix_high` the highest pitch any other track plays
`is_downbeat` true on beat 1 of a bar
`is_offbeat` true between the beats, the syncopated spots
`position_in_bar` how far through the bar you are, 0 to 1
`has_harmony` true when the progression has a chord at this spot
`key` the song's key as text, for example `"C Major"`
`whole` `half` `quarter` `eighth` `sixteenth` note lengths in beats, so durations read musically

Available only inside on source_note, describing the master note being handled:

Value Meaning
`source_pitch` that note's pitch
`source_beat` where it sits, in beats from the start of the clip
`source_length` how long it is, in beats
`source_velocity` how hard it is played, 0 to 1
`source_bar` which bar it falls in, starting at 1
`source_beat_in_bar` its beat within that bar, starting at 1

Reading a source_ value outside on source_note is an error, and the console tells you which line.

Holding a pad across a repeated note

on source_note runs once for every master note, so when the master holds a note by repeating it, three quarter-note C's in a row, a plain pad would retrigger three times. Two functions fix that, both usable only inside on source_note.

Function Meaning
`source_run_length(max_gap?)` beats from this note through a run of the same repeated note
`is_run_start(max_gap?)` true when this note begins such a run

source_run_length() measures the whole stretch the same pitch keeps sounding, and is_run_start() is true only on the first note of that stretch, so you place one long pad and skip the repeats:

# One sustained pad per run, however many times the master repeats the note.
on source_note
    if is_run_start()
        emit_note(source_pitch, source_beat, source_run_length(), source_velocity * 0.5)
    end
end

For a note that is not repeated, source_run_length() is just that note's own length and is_run_start() is true, so the same line handles runs and single notes without a branch on the duration.

A different note ends a run, and by default so does any gap, since only touching notes join. If the master plays the pitch staccato, short notes separated by short rests, pass a max_gap in beats to bridge those gaps so the burst reads as one run. Use the same max_gap in both calls:

# Merge a staccato burst of the same note into one held pad.
on source_note
    if is_run_start(sixteenth)
        emit_note(source_pitch, source_beat, source_run_length(sixteenth), source_velocity * 0.5)
    end
end

The master still plays its own notes underneath. The script only reads them, it does not replace them, so the pad sits on the generated track alongside the master. That is why the examples drop the velocity, to tuck it behind. To hear only the pad, mute the master track.

Available only inside on source_chord and on harmonic_chord, describing the chord being handled:

Value Meaning
`chord_root` the chord's root, as a pitch
`chord_quality` the chord's flavour as text, such as `"major"` or `"dom7"`
`chord_size` how many notes the chord has, 3 for a triad, 4 for a seventh
`chord_beat` where the chord starts, in beats
`chord_length` how long it lasts, in beats

Functions

Notes and output

Function What it does
`emit_note(pitch, beat, length?, velocity?)` place a note, described above
`mute(on)` mute or unmute this track, the only way to mute a generated track
`log(value)` write a line to the console, handy for peeking at a value

Fitting into your mix

A script can see the other tracks, not just its master, so it can add to the mix instead of piling onto a spot that is already taken.

Function What it does
`place_open(pitch)` the same note, moved whole octaves into a register your mix leaves open
`mix_crowded(pitch)` true when other tracks are already busy around this pitch
`mix_busy_at(beat)` true when another track already plays on this beat

place_open makes the decision for you. mix_crowded and mix_busy_at hand you the same information so you can decide yourself. Drums do not count as pitches, so they never make a register look crowded, but they do count for mix_busy_at, because that one is about rhythm.

# A shimmer an octave up, but only where the mix has room for it.
on source_note
    var pitch = place_open(source_pitch + 12)
    emit_note(pitch, source_beat, source_length, source_velocity * 0.5)
end
# Fill the beats nothing else is playing on.
on beat
    if not mix_busy_at(beat_index)
        emit_note(72, beat_index, 1)
    end
end

Numbers

Function What it does
`random()` a number from 0 to 1, reproducible
`min(a, b)`, `max(a, b)` the smaller or larger of two numbers
`clamp(value, low, high)` keep a value within a range
`round(x)`, `floor(x)`, `ceil(x)` round to nearest, down, or up
`abs(x)` distance from zero
`sin(x)`, `cos(x)` smooth, wavy motion, for a slow swell

Chords

Inside on source_chord and on harmonic_chord.

Function What it does
`chord_tone(n)` a note from the chord: 1 is the root, 3 the third, 5 the fifth, 7 the seventh
`arp_tone(step, octaves)` walks the chord's notes for an arpeggio, climbing through octaves

Harmony

Available anywhere.

Function What it does
`nearest_harmony_tone(pitch)` nudges a pitch to the closest note of the chord playing here

Scale and key

The song must have a key.

Function What it does
`scale_degree(n)` a note from the key's scale: 1 is the home note
`in_key(pitch)` true when a pitch belongs to the song's key
`snap_to_key(pitch)` pulls a pitch to the nearest note in the key

Rhythm and feel

Function What it does
`subdivide(n)` the length of one of n equal parts of a beat
`euclid(step, pulses, steps)` an even rhythm: true when this step is one of the spread-out hits
`metric_accent(beat)` how strong a beat position feels, 0 to 1, downbeats heaviest. Good for velocity.
`humanize(value, amount)` adds a little random wobble for a less mechanical feel
`chance(p)` true that fraction of the time, so `chance(0.25)` fires a quarter of the time
`pick(a, b, ...)` picks one of the given values at random

Writing logic

You can name values, branch, and repeat.

Name a value with var, then reuse it. Assign again with var to change it.

on source_note
    var lift = 12
    emit_note(source_pitch + lift, source_beat, source_length, source_velocity * 0.5)
end

Branch with if, else and end. Conditions use the usual comparisons and combine with and, or and not, with true and false as the yes and no values.

# Only shadow the loud notes, and put quiet ones lower.
on source_note
    if source_velocity > 0.7
        emit_note(source_pitch + 12, source_beat, source_length, source_velocity * 0.5)
    else
        emit_note(source_pitch - 12, source_beat, source_length, source_velocity * 0.4)
    end
end

Repeat a block a fixed number of times with repeat count. Add as i to number each pass, starting at zero, so every repeat can differ. That is the key to arpeggios and subdivisions.

# Four on the floor: a note on every beat of every bar.
on bar
    repeat 4 as i
        emit_note(36, 1 + i, 1, 0.9)
    end
end

Comments start with # and run to the end of the line.

Muting

A generated track owns its own output, so the mixer's mute does not apply. A script is the only way to silence it.

on init
    mute(true)
end

When something is wrong

The editor checks the script as you type. A moment after you stop, the console shows how many notes it would produce, or the problem and the line it is on. Click the error and the cursor jumps there, switching handler first if the error is in another one.

A broken script never half-fills the track. If it cannot run, the track comes out empty and unmuted, and the reason is in the console, so a typo is always visible rather than silent. A couple of forgiving touches keep small slips from becoming errors: dividing by zero gives zero rather than failing, and a pitch or beat outside the clip is skipped.

A few things to know

random() is reproducible. It is seeded from the track, so the random part lands the same way every time the track recomputes and your part does not reshuffle itself behind your back.

There is no remainder operator and no custom functions. Keep scripts to the words on this page.

A script can read the song's chords, whether played or from the progression, and its key. The built-in generators, counterpoint and full pad voicing among them, are not available as functions.

The shipped examples

Pick one when you add the track, then change it in the editor.

Octave shimmer is a soft layer an octave above each master note. Open shimmer is the same idea moved to wherever your mix still has room, one line different from Octave shimmer, so comparing the two shows exactly what place_open does. Fifth above is a fuller companion line a fifth up. Octave stack places one and two octaves above for a wide, airy doubling. Delayed echo is a softer fifth one beat later, a simple call and response.

See generated tracks for how to add one, choose its master, and audition it.