Master Class: Scripting in Kontakt 3

Use Kontakt's powerful script language to create custom instrument control panels and advanced MIDI processors.BONUS MATERIALWeb Clips: download a ZIP archive of all scripts covered in this Master Class, and more!
Image placeholder title
Image placeholder title

A brief spin through the factory instrument library reveals the power of the Kontakt Script Processor (KSP). All the factory instruments have performance control panels that are visible when the rack is in Multi Instrument mode, and these panels are courtesy of scripting. Scripts provide access to essential controls as well as many forms of advanced event processing. Examples of this processing include automatic chord generation, step sequencing, arpeggiation, and much more.

This month's “Square One” column, “Read the Script,” gives an introduction on how to script using basic examples drawn from three software programs, one of which is Kontakt 3. In this “Master Class,” I'll pick up where “Square One” leaves off and reveal some of the power of KSP scripting. If you're new to scripting, I recommend reading “Square One” first. You'll find an archive of all the scripts from this article in Web Clip 1.

Before You Type

A lot of effort has been put into KSP scripting by Native Instruments as well as third-party developers, and they have created many useful scripts that are there for the taking. Kontakt 3 comes with a library of scripts of various kinds, and you'll find some useful scripts in the Kontakt User Library on the NI Web site ( Although scripts for third-party Kontakt instruments are often locked, you'll occasionally come across ones that are not. If you don't find exactly what you want, you may still get some useful hints.

It's a good idea to start your own library of script fragments. An example of how to add a fragment would be to take the several lines needed to declare a user interface element, position that element on the panel, name it, and set a default value for it. Filling in a few placeholders with the relevant data is much faster than retyping those three or four lines five or ten times.

Kontakt's Script editor is adequate for creating and testing short scripts, but using it for more-complex scripting can be tedious. Using an external text editor will save you lots of time, and there's no better choice than Nils Liberg's free KScript Editor ( While at his site, check out his collection of scripts and his excellent Kontakt scripting tutorial. You'll also find an active Kontakt forum with lots of scripting information at Virtual Instruments Composers Forum (

Keep in mind that the script language is case sensitive and spaces are ignored. If you break up a long statement with “…”, it will still be processed as a single line. Comments, which are enclosed in curly brackets, do not slow down script execution, but they can make scripts cluttered. On the other hand, comments are very helpful when you revisit a script — so use them, but only sparingly.

Getting Gooey

As mentioned, creating control panels for use in Multi Instrument mode is an important application of scripting, and the process consists of several parts. First, you need to design the panel layout, which entails defining its individual controls, positioning them as desired, specifying how they operate (by setting value ranges, display units, text messages, and so on), and ensuring that they're visible in Multi Instrument mode. (You can always access script controls in Instrument Edit mode by clicking on the Script Editor tab.) Next, you need to link script controls to Kontakt instrument parameters, and that linking may or may not be bidirectional. You may want to set up MIDI remote control for panel elements. Finally, you might want to hide or show some elements depending on certain conditions or the settings of other elements. For instance, panels frequently have a button for toggling between easy and expert modes.

Image placeholder title

FIG. 1: GUI element declarations result in an ad hoc control panel that has enigmatic labels.

Graphical user interface elements are defined using declare statements in the init callback, as are other script elements such as variables, constants, and tables. Six types of GUI controls are available: buttons, knobs, numericals, menus, labels, and tables. See the sidebar “Script Examples,” example A, for an init callback that defines one of each.

If you try this script, you'll see that it leaves a lot to be desired (see Fig. 1). The graphical arrangement of the controls is ad hoc, the display names are the same as the declared names, and the panel disappears when you switch to Multi Instrument mode.

Script control panels are six rows wide by six columns high. You position elements with a move_control statement: move_control ($knob,1,2) would place the knob in column 1 of row 2. Setting either position to 0 hides the control. You can reposition controls in any callback, but it's a good idea to set their position in the init callback even if you plan to move them later in the script (for instance, when switching display modes).

You change the names of knobs, buttons, and numericals with the set_text statement: set_text ($knob,“Volume“). Although names can be any length, depending on the font, knobs typically display 7 characters, whereas buttons and numericals display 14. Use the set_text statement along with the add_text_line statement to fill in labels. The add_menu_item statement adds items to a menu and sets the values returned when items are selected. The order of the statements determines the order of the items in the menu. For an init callback that illustrates these points, see example B in “Script Examples.”

The first statement, make_perfview, causes the script's GUI to appear in Multi Instrument mode (see Fig. 2). The wood-panel background is not the result of scripting; rather, it is set in the Instrument options. But once you've selected a graphic in the Skin Bitmap box, the _set_skin_offset statement sets the vertical offset in pixels for the top of the panel. Skins should be Targa files that are 633 pixels wide; vertical offsets should be multiples of 225. So you need a 633 × 1,125-pixel graphic to have separate panels for each of the five scripts an instrument can hold.

Notice that button, knob, and numerical values are set with the := statement, as are variable values. Several unit options are available for knobs (I've used milliseconds in example B), and you can set a default value that applies when the init callback is executed or when you right-click on the knob.

So What?

There's not much point to a pretty control panel unless it controls something. That's where the other callbacks come in. Incoming MIDI note events trigger on note and on release callbacks. Incoming MIDI controller messages trigger on controller, on rpn, and on nrpn callbacks. Interacting with a script's GUI triggers on ui_control callbacks (there is a separate one for each declared control). Interacting with other Kontakt GUI controls triggers the on ui_update callback. That's how you establish 2-way communication between script and Kontakt parameters.

Only the init callback is required (unless the script is empty, of course). Other callbacks are used as needed for the task at hand. You can have more than one instance of the same callback, but it's better practice to use conditional branching within a callback to differentiate tasks triggered by the same type of action. See “Script Examples,” example C, for the callback that's associated with the button in Fig. 2.

The ui_control callback is triggered whenever the button is clicked. Clicking on the button named Press Me makes the knob disappear and renames the button to Show Knob. Clicking on the button again makes the knob reappear and renames the button to Hide Knob. Use variations on this theme to toggle between easy and expert panels or to manage more GUI elements than fit on a single panel.

Click to continue reading "Master Class: Scripting in Kontakt 3"

Blind Update

Script GUI elements are often used to access settings inside a Kontakt instrument. The simplest case is a script knob that sets an instrument knob. See “Script Examples,” example D, for the init and ui_control callbacks that control the instrument's Group 1 Source volume. The script knob displays volume in decibels, just as the instrument knob does.

An example of a built-in variable is $ENGINE_PAR_VOLUME. Built-in variables are automatically updated to reflect Kontakt engine settings, and you'll find variables for most parameters. Built-in variables are always uppercase, so your own variables will be easier to spot if you combine uppercase and lowercase letters.

Image placeholder title

FIG. 2: A little bit of formatting and renaming, as well as adding a custom skin, makes the script GUI easier to use.

Scripting can apply on the instrument or the group level. The last three numbers in the _set_engine_par and _get_engine_par commands (0,-1,-1 in example D) determine the group, slot, and module type affected. Group and slot numbering starts at 0; use a group value of -1 to access instrument-level parameters. (In the case of volume, setting the group to -1 addresses the main instrument volume.) Slot is used to select among multiple send and insert effects and modulators. Use a slot value of -1 when that doesn't apply. The last setting distinguishes instrument insert (1) versus send (0) effects or modulator routing. Use a value of -1 in all other cases.

When you have an instrument with several groups and you want to control a parameter (group volume, for instance) that appears in all groups, it's easy to set up a menu to select the group, and then use the same knob to control each group's volume. You'll find an example on p. 66 of The Kontakt Script Language manual that uses a group menu with volume, pan, and tuning controls (see Fig. 3). For convenience, I've included the script in Web Clip 1.

For 2-way communication, in which changes to the instrument knob are reflected in the script GUI, add a ui_update callback. That should be done with care, however, because ui_update is triggered whenever any instrument control is moved, and frequent callbacks may degrade performance (see “Script Examples,” example E).

If you want to use MIDI continuous controllers for script GUI elements, you need to use the controller callback. That's because you can't assign MIDI modulators directly to script GUI elements in the way that you can with instrument controls. As with the ui_update, the controller callback should be used with care; lots of incoming MIDI CC data can degrade performance, and it's almost always preferable to use Kontakt's robust MIDI-remote scheme. For how to map the 1-way volume knob to MIDI CC 11, see “Script Examples,” example F.

The ignore_controller command blocks the MIDI CC message from affecting all other instrument parameters. The $Volume value calculation ($Volume := …) converts the MIDI CC range of 0 to 127 to the volume knob's range of 0 to 1,000,000. Keep in mind that the KSP uses integer arithmetic; therefore, large value ranges are used to get high resolution. Time parameters, for instance, are scaled to microseconds, so a declared range of 0 to 1,000,000 in a script corresponds to a time range of 0 to 1 second.

Take Note

Note processing adds a new, and arguably more interesting, dimension to scripting. The factory library is full of note-processing scripts, and in the spirit of not reinventing the wheel, I've used one and scavenged another for the instrument shown in Fig. 4. It combines the Easy Chord Generator script (top of Fig. 4) from the Kontakt 2 library with a custom script that can either correct incoming notes or constrain them to a user-defined scale (bottom of Fig. 4). You may prefer to think of scale correcting as mode shifting (see Web Clip 2).

Notice that the chord generator script comes first. That illustrates an important point about scripting: each instrument holds up to five scripts, and they are executed in the left-to-right order of the tabs that select them. In particular, MIDI data entering the instrument passes through the scripts in that order. The chord generator needs to come before the scale fixer in the processing chain so that all chord notes, rather than just the incoming MIDI notes, are scale corrected.

Image placeholder title

FIG. 3: Use a menu to have the same GUI elements control different groups.

The scale fixer uses two 12-column tables. The upper table is one row high and serves to pass or reject notes matching the pitches in the legend above it. The cells in the lower table can have a value of either 0, 1, or -1, which indicates that the corresponding note will be either unchanged or shifted up or down a semitone. The settings in Fig. 4 allow all notes through but shift notes with accidentals down a semitone — in other words, the scale shifter corrects incoming notes to the key of C major.

The Root knob shifts the legend and, along with it, the scale to which the notes are corrected. The script for that was scavenged from the Fiddle instrument in the Kontakt 3 factory World library. To view the script for the scale correction, see “Script Examples,” example G.

Every incoming MIDI message is assigned an ID, and the ignore_event command prevents the incoming note from being played automatically. The variable $pitcla holds the pitch class (0 for C, 1 for Db, 2 for D, and so on) of the incoming note, and the variable $nupitcla is the pitch class of the note after the script adjusts for the root key. Because that calculation involves subtracting the pitch class indicated by the value of the Root knob, negative values can result. The first if…end if condition corrects for that. The second if…end if condition throws away rejected notes and plays passed notes with corrected pitches.

The declarations and clever handling of the note-names display and Root knob are lengthy but self-explanatory. They set the value of the variable $key_knob to the pitch class of the root key. That number is used to both rotate the note-names display and adjust the value of $nupitcla before the tables are applied. To see how this works, look at the script in Web Clip 1.

For clarity I've chosen short script examples to illustrate key points. Scripts are often huge and include a lot of repetition. Careful formatting and commenting makes them more readable. Keeping an archive of oft-repeated routines speeds up script writing. Above all, unless you're a glutton for syntax, keep the excellent manual handy.

Len Sasso is an associate editor of EM. For an earful, visit his Web site

Image placeholder title

FIG. 4: In this 2-script instrument, the first script (top panel) turns notes into chords, and the second script (bottom panel) keeps those chords within a user-defined scale or mode.

Script Examples

Script Example A:

on init
declare ui_button $button
declare ui_knob $knob (0,127,1) {(lo,hi,display divisor)}
declare ui_value_edit $numerical (0,127,1){(lo,hi,display divisor)}
declare ui_menu $menu
declare ui_label $label (1,2){(width,height)}
declare ui_table %table[16] (3,2,-24) {[size],(width,height,range)}

end on

Script Example B:

on init
make_perfview {view in Multi Instrument}
declare ui_button $button
move_control ($button,1,4) {(col,row)}
set_text ($button,“Press Me“)
declare ui_knob $knob (0,1000000,1000) {(lo,hi,display divisor)}
move_control ($knob,1,2)q
set_text ($knob,“Big Knob“)
set_knob_unit ($knob,$KNOB_UNIT_MS)
set_knob_defval ($knob,5000)
declare ui_value_edit $numerical (-63,64,1){(lo,hi,display divisor)}
move_control ($numerical,1,1)
set_text ($numerical,“Double Click“)
$numerical := 0
declare ui_menu $menu
move_control ($menu,2,1)
add_menu_item ($menu,“First“,4)
add_menu_item ($menu,“Second“,7)
add_menu_item ($menu,“Third“,11)
add_menu_item ($menu,“Fourth“,13)
declare ui_label $label (2,2){(width,height)}
move_control ($label,3,1)
set_text ($label,“Label line one.“)
add_text_line ($label,“Label line two.“)
declare ui_table %table[12] (4,2,-1) {[size],(width,height,range)}
move_control (%table,3,3)

end on

Script Example C:

on ui_control ($button)
if ($button=1)
move_control ($knob,0,2)
set_text ($button, “Show Knob“)
move_control ($knob,1,2)
set_text ($button, “Hide Knob“)
end if

end on

Script Example D:

on init
declare ui_knob $Volume (0,1000000,1)
set_knob_unit ($Volume,$KNOB_UNIT_DB)
set_knob_defval ($Volume,630859)
$Volume := _get_engine_par ($ENGINE_PAR_VOLUME,0,-1,-1)
set_knob_label ($Volume,_get_engine_par_disp($ENGINE_PAR_VOLUME,0,-1,-1))

end on

on ui_control ($Volume)

set_knob_label ($Volume,_get_engine_par_disp($ENGINE_PAR_VOLUME,0,-1,-1))

end on

Script Example E:

on ui_update
$Volume := _get_engine_par ($ENGINE_PAR_VOLUME,0,-1,-1)

end on

Script Example F:

on controller
if ($CC_NUM = 11)
$Volume := (%CC[11]*1000000)/127
set_knob_label ($Volume,_get_engine_par_disp($ENGINE_PAR_VOLUME,0,-1,-1))
end if

end on

Script Example G:

on note
ignore_event ($EVENT_ID)
$pitcla := $EVENT_NOTE mod 12
$nupitcla := ($pitcla-$key_knob) mod 12
if ($nupitcla < 0)
$nupitcla := $nupitcla + 12
end if
if (%gates[$nupitcla]=0)
play_note ($EVENT_NOTE + %table[$nupitcla], $EVENT_VELOCITY,0,-1)
end if

end on

Additional Resources

Kontakt Users Library — Scripts

Nils Liberg free KSP script editor, library of scripts, and excellent tutorial

Virtual Instruments Composers Forum KSP scripting forum

Web Clips: download a ZIP archive of all scripts covered in this Master Class, and more!