Skip to content

Keymaps


We have flows, we have a PlayEvent, but there's a missing piece: how does the framework know which flow to use when a key is pressed? Without that mapping, every key would do the same thing -- or nothing at all.

Keymaps connect MIDI keys to flows. They tell the framework: "when this key is pressed, send the voice into this flow." They also control the visual appearance of keys in Kontakt's keyboard display -- their colors, types, and labels.

Declaring a Keymap


Keymaps are declared inside cb Keymaps, which runs during every Reload (preset/snapshot load). This means keymaps are rebuilt from scratch each time a preset loads, giving you the flexibility to change key assignments based on the current state of the instrument.

Here's the basic setup:

node MyKeyboard:
    cb Keymaps:
        define KEYMAPS += my_keys

        { Map keys C2-C4 (MIDI 36-60) to my_flow }
        declare i
        for i := 36 to 60
            keymap.request(my_keys, 0, i, ...
                           my_flow, ...
                           NI_KEY_TYPE_DEFAULT, KEY_COLOR_BLUE, "")
        end for
end node

define KEYMAPS += registers your keymap category name, just like FLOWS and IVLS_NODES. You can have multiple categories in the same instrument.

keymap.request


keymap.request() is the function that assigns a key. Its arguments are:

keymap.request(category, map_index, key, flow, type, color, name)
  • category -- the keymap category name you registered with KEYMAPS
  • map_index -- which map to register on (use 0 for a single-map instrument)
  • key -- the MIDI key number (0-127)
  • flow -- the flow to send voices into when this key is pressed
  • type -- the Kontakt key type (controls how Kontakt displays and handles the key)
  • color -- the Kontakt key color
  • name -- an optional display name for the key

Key Types


Kontakt provides several key types that affect how a key behaves in the keyboard display:

  • NI_KEY_TYPE_DEFAULT -- a standard playable key
  • NI_KEY_TYPE_CONTROL -- a control/keyswitch key (displayed differently)
  • NI_KEY_TYPE_NONE -- an inactive key (no visual indicator)

Key Colors


IVLS supports all of Kontakt's key colors:

  • KEY_COLOR_BLUE -- typically used for playable ranges
  • KEY_COLOR_RED -- often used for keyswitches or selection keys
  • KEY_COLOR_GREEN -- often used for the currently selected/active key
  • KEY_COLOR_CYAN -- used for highlighting special keys within a range
  • KEY_COLOR_INACTIVE -- grayed out, indicating the key does nothing

The choice of color is purely visual -- it doesn't affect behavior. Use colors to communicate the purpose of each key region to the user.

Multi-Zone Keyboards


Production instruments rarely use a single uniform key range. Instead, they divide the keyboard into zones, each with its own purpose and flow. A common pattern used in instruments like Sylvan looks like this:

node MyKeyboard:
    cb Keymaps:
        define KEYMAPS += my_keys

        declare i

        for i := 0 to 127
            if in_range(i, 36, 84)
                { Play range: these keys produce sound }
                keymap.request(my_keys, 0, i, ...
                               my_play_flow, ...
                               NI_KEY_TYPE_DEFAULT, KEY_COLOR_BLUE, "")

            else if in_range(i, 24, 35)
                { Selection range: these keys select a sound/preset }
                keymap.request(my_keys, 0, i, ...
                               my_select_flow, ...
                               NI_KEY_TYPE_CONTROL, KEY_COLOR_RED, "")
            else
                { Inactive: fill remaining keys }
                keymap.request(my_keys, 0, i, ...
                               my_no_play_flow, ...
                               NI_KEY_TYPE_NONE, KEY_COLOR_INACTIVE, "")
            end if
        end for
end node

This gives you a play range (blue keys that trigger sounds), a selection range (red keys that act as controls), and inactive keys (grayed out) filling the rest of the keyboard.

Each zone points to a different flow, so pressing a key in the play range triggers different behavior than pressing a key in the selection range.

Keymap Rebuilding


Because cb Keymaps runs inside every Reload, keymaps are rebuilt from scratch each time a preset loads. The framework calls keymap.reboot() internally, which clears all existing key assignments and re-runs every node's cb Keymaps callback.

This means your keymap setup code can safely depend on the current state of the instrument. If a preset changes which notes are available, the keymap will reflect that on the next reload.


With keymaps in place, pressing a key will create a voice, route it into the correct flow based on the key mapping, and ultimately trigger a sound through your PlayEvent. You now have all the pieces for a working instrument: nodes, flows, a PlayEvent, and keymaps.