Skip to content

Serialization


When a user saves a DAW project, closes it, and reopens it three months later, they expect the instrument to sound exactly the same. Every knob position, every selected preset, every enabled layer -- all of it needs to be restored. This is serialization: saving the instrument's state so it can be perfectly reconstructed later.

IVLS handles most of this automatically, but it's important to understand what happens behind the scenes so you know where to put your own logic when you need it.

Engine Box Parameters Are Automatic


If you've set up your engine box correctly (as described in the Engine Box page), your parameter values are already serialized. The pers keyword on the internal data array means Kontakt saves those values into snapshots and NKI files automatically. When a preset loads, Data.PCCB() restores everything: it validates ranges, fires par_cb for every parameter, and syncs all UI controls.

You don't need to write any save/load code for engine box parameters. They just work.

Instance Persistence with instpers


Some state needs to persist across the life of the NKI but should not be included in snapshots. Things like:

  • Which page the user is currently viewing
  • Which layer tab is selected
  • Scroll positions and view state

This is what instpers is for. Variables declared with instpers are saved into the NKI file itself, but they're excluded from snapshot save/restore. So switching snapshots won't change which page the user is looking at, but closing and reopening the NKI will remember it.

declare instpers current_page := 0
declare instpers selected_layer := 0

Use instpers for anything that's about the view rather than the sound.

The Reload Split: cb Reload and cb PostReload


When a preset or snapshot loads, IVLS runs two phases of callbacks:

cb Reload runs first. This is where data restoration happens. Engine boxes call Data.PCCB() here, keymaps are rebuilt, and any state-dependent setup runs. All nodes' cb Reload callbacks execute before any cb PostReload callbacks.

cb PostReload runs after all cb Reload callbacks have finished. This is where UI refresh happens. The framework automatically calls uir.update() here, which runs all registered UI routines. By this point, all parameter values are restored and all state is consistent, so the routines can safely read the current state and update the display.

This two-phase split prevents a common bug: trying to update a UI element before the data it depends on has been restored. If your layer display routine runs before the engine box has restored which layer is selected, it would show stale data. The Reload/PostReload split guarantees the right order.

NKA Files for External Data


Engine box parameters cover most needs, but some instruments store additional data externally -- custom mappings, user sample assignments, calibration tables. For this, IVLS provides the serialization component (serialize.ksp), which wraps Kontakt's save_array() / load_array() in a managed system with async completion tracking.

The details of the serialization component are beyond the scope of this page, but the basic idea is simple: you write data into a memory array, save it to an .nka file, and load it back later. The framework handles the async callbacks and error states.

The Snapshot Type 3 Gotcha


One thing to be aware of: Kontakt's snapshot system uses type 3 persistence, which means pers variables are not zeroed on init. When your script first loads, pers variables retain whatever value was last saved. This is usually fine -- Data.PCCB() validates and processes everything during reload. But if you're working with pers variables outside of engine boxes, be aware that they may contain stale data from a previous session rather than your declared initial value.

The engine box system handles this gracefully through its range validation and version checking. If you're managing persistence manually, you'll need to account for it yourself.

Version Tracking


Engine boxes include built-in version tracking through get_current_version() and check_version.response(). When a preset saved with version 0 is loaded into an instrument now at version 1, the check_version.response() function receives the old version number, giving you a chance to migrate data -- remapping parameter indices, applying default values for new parameters, or adjusting ranges.

The full serialization system -- including Data IDs, Product IDs, and multi-version migration chains -- is an advanced topic covered in later units.


For most instruments, serialization is something you set up once and forget about. Engine boxes handle parameter persistence, instpers handles view state, and the Reload/PostReload split keeps everything in the right order. The framework does the heavy lifting so you can focus on what the instrument actually does.