Building a KSP Plugin to Control a Bass Instrument in Kontakt — Every Pitfall Documented
A friend of mine plays fretless bass, and we have a long-term goal: record multisamples of their playing and turn it into an original Kontakt instrument. But sampling takes time and preparation. So as a first step, I decided to build a KSP script plugin to control an existing bass instrument — learning the tooling before the real recordings happen.
This is the record of writing that KSP (Kontakt Script Processor) script: implementing portamento, slide-in, and vibrato, and everything that went wrong along the way.
What I Did — and What's Next
This time: Wrote a KSP script on top of an existing bass sample library, adding portamento, slide-in, and vibrato as controllable parameters.
Next step: Have my friend record the fretless bass as multisamples, load them into Kontakt, and use this same script as the instrument's engine.
Features implemented in the script:
- Portamento — glide from the previous note to the current one (configurable time and legato mode)
- Slide-in — approach each note from a few semitones below or above before settling on pitch
- Vibrato — CC1 mod wheel overrides the depth knob when active
What Is KSP?
KSP (Kontakt Script Processor) is the scripting language built into NI Kontakt. You write callbacks like on note, on release, and on init to handle pitch changes, CC responses, and custom UI.
There are two places you can load a script, and this distinction matters enormously:
| Slot | Location | Capabilities |
|---|---|---|
| Instrument Script | Wrench icon → Script Editor | on note, on release, change_tune all work |
| Multi Script | KSP slot in the Multi rack | on note and change_tune are not available |
I spent about an hour confused because I had pasted my script into the Multi rack slot. Everything I tried to do either silently did nothing or threw errors.
Every Error I Hit
1. syntax error on line 1 — Japanese comments
I added a few Japanese comments at the top of the script. Kontakt refused to load it at all:
syntax error on line 1
Cause: KSP's parser doesn't handle non-ASCII characters (or BOM-prefixed UTF-8) in comments reliably.
Fix: Remove all non-ASCII characters from the script. Comments in ASCII only, or no comments at all.
{ ポルタメントの処理 }
on note
on note
2. Multi Script vs Instrument Script
The most time-consuming mistake. I loaded the script in the Multi rack's KSP slot instead of the instrument's own script slot. The error message when using change_tune:
change_tune() cannot be used in a multi script!
And on release simply doesn't exist in a multi script context — the callback is rejected with a red underline.
Fix: Open the instrument, click the wrench icon, go to Script Editor, and paste into one of the script slots there. The Multi rack slot is for a completely different use case.
3. Pitch unit is millicents — 1 semitone = 100,000
change_tune takes pitch in millicents. I had written 1000 thinking it was one semitone, which made portamento technically functional but inaudible (it was sliding by 1/100th of a semitone).
declare const $SEMITONE := 1000
declare const $SEMITONE := 100000
The math: 1 semitone = 100 cents = 100,000 millicents.
4. declare ui_knob syntax
The KSP declaration syntax is declare ui_knob $name (min, max, display_ratio). A label string does not go here.
declare ui_knob $knob_porta_time ("Time", 0, 2000, 1)
declare ui_knob $knob_porta_time (0, 2000, 1)
set_text($knob_porta_time, "Time")
The same applies to declare ui_switch and declare ui_menu — always separate the declaration from the text assignment.
5. declare polyphonic — per-note variables
KSP has no declare local. Variables declared inside a callback don't persist across note events. To hold a value that's independent per note (like the note number, current pitch offset, vibrato phase), declare it in on init using declare polyphonic:
on init
declare polyphonic $note_num
declare polyphonic $offset
declare polyphonic $phase
end on
on note
$note_num := $EVENT_NOTE
{ $note_num is independent for each simultaneous note }
end on
Related: in on release, $EVENT_NOTE is not accessible. Instead, save it in on note via a polyphonic variable and read that in on release.
on release
%held[$note_num] := 0 { $note_num saved in on note }
end on
6. CC array is %CC[], not $CC[]
% prefix means integer array in KSP. The mod wheel value is at %CC[1], not $CC[1].
if ($CC[1] > 0)
if (%CC[1] > 0)
Building the UI
make_perfview activates the Performance View, and move_control($var, col, row) places controls on a grid. col = 0 hides a control.
on init
make_perfview
set_ui_height_px(104)
declare ui_label $lbl_porta (1, 1)
set_text($lbl_porta, "PORTA")
set_control_par(get_ui_id($lbl_porta), $CONTROL_PAR_TEXT_ALIGNMENT, 1)
move_control($lbl_porta, 1, 1)
declare ui_knob $knob_porta_time (0, 2000, 1)
set_text($knob_porta_time, "Time")
set_knob_unit($knob_porta_time, $KNOB_UNIT_MS)
set_knob_defval($knob_porta_time, 150)
$knob_porta_time := 150
move_control($knob_porta_time, 1, 2)
end on
Custom Background Image
To set a background image for the Performance View:
set_control_par_str($INST_WALLPAPER_ID, $CONTROL_PAR_PICTURE, "wallpaper")
The image must live in a [InstrumentName] Resources/pictures/ folder next to the .nki file. PNG and TGA both work. Minimum width is 633px; height should be 68 + the value passed to set_ui_height_px.
A companion .txt file with the same name is required:
Has Alpha Channel: yes
Number of Animations: 0
Horizontal Animation: no
Vertical Resizable: no
Horizontal Resizable: no
Fixed Top: 0
Fixed Bottom: 0
Fixed Left: 0
Fixed Right: 0
The Finished Script
GitHub: NakahodoRintaro/kontakt_test
Portamento, slide-in, and vibrato all working. The UI has six knobs and two switches arranged in a grid with section labels.
Summary
| Problem | Cause | Fix |
|---|---|---|
syntax error on line 1 | Non-ASCII characters in comments | ASCII-only comments |
change_tune not available | Script in Multi rack slot | Move to instrument script slot |
| Portamento inaudible | Used 1000 for one semitone | Use 100000 (millicents) |
ui_knob shows red | Label string in declaration | Use set_text() separately |
Can't read note in on release | $EVENT_NOTE invalid there | Save with declare polyphonic in on note |
| CC value always zero | Used $CC[1] | Use %CC[1] |
The official KSP Reference Manual PDF is the most reliable source. Whenever something turns red, the first thing to check is the exact syntax definition and unit specification in the manual.
Live with a Smile!
