ICST Kristall Motion Map — User Guide

Level: Intermediate | Audience: Composer, sound designer, spatial-audio technician. | Version: 2.2.8

ICST Kristall Motion Map is a standalone REAPER Lua script with a real-time graphical interface. It arranges up to 64 AmbiEncoder sources as points within a 3D crystal lattice and moves them through space using a step sequencer. The movement can be tracked live in the isometric preview, sent via OSC to an ICST AmbiEncoder_64, and shaped using instance-specific transformations, quantisation, smoothing and interaction. This instrument was created in collaboration with and inspired by Eli Stine, a guest at the ICST Studio Residency 2026.

ICST Kristall Motion Map — full interface overview showing instance list, lattice preview, parameter panel and status bar

This is a short demo of a random Kristall by Eli Stine — rendered in binaural.

Warning
🎧 Listen with headphones. This recording is binaural — the spatial effect is lost on speakers.
Update
New to Kristall? Skip to §13 First session walkthrough and get your first preset running in under five minutes — then come back here for detailed reference.

1. Requirements

  • REAPER v6 or later (v7 recommended)
  • ICST AmbiEncoder_64 on the target track — see Installation
  • Python 3 with python-osc — required only for the live OSC preview bridge

2. Installation

Step 1 — Download the bundle

Download ICST_Kristall_Motion_Map_Bundle.zip (also available on the Downloads page). Unzip it anywhere on your computer — for example ~/REAPER/Scripts/ICST_Kristall_Motion_Map_Bundle/.

The bundle contains:

  • scripts/JS_ICST_Kristall_Motion_Map.lua — the main script
  • jsfx/JS_ICST_Kristall_Controller.jsfx — optional MIDI/JSFX control surface
  • README.md — quick-start instructions

Step 2 — Load as a ReaScript

  1. In REAPER, go to Actions → Load ReaScript…
  2. Navigate to JS_ICST_Kristall_Motion_Map.lua and click Open.
  3. REAPER adds the script to the Actions list. Run it once — the Kristall Motion Map window opens.

Python on Windows

Warning
Windows users: REAPER often cannot find Python automatically. Follow these three steps after installing Python from python.org/downloads.

Step 1 — Find your Python path. Open a Command Prompt and run:

python -c "import os, sys; print(os.path.dirname(sys.executable))"

Example output: C:\Users\stine\AppData\Local\Python\pythoncore-3.14-64

Step 2 — Point REAPER to Python. Go to Preferences → Plug-ins → ReaScript, enable “Force ReaScript to use specific Python .dll…”, and point it to the python3xx.dll inside that folder (e.g. python314.dll).

Step 3 — Install python-osc using the same Python:

python -m pip install python-osc

Step 3 — Optional: launcher file

If you prefer to keep the script in a version-controlled folder, create a one-line launcher anywhere in your REAPER Scripts directory:

-- JS_ICST_Kristall_Motion_Map_Launcher.lua
dofile('/path/to/JS_ICST_Kristall_Motion_Map.lua')

Load this launcher as the ReaScript instead. To reload after editing the main script, close the Kristall window, then re-run the launcher action.


3. The interface at a glance

ICST Kristall Motion Map in action — lattice preview with 8 cubic sources stepping through space

ICST Kristall Motion Map — full window overview showing instance list, lattice preview, parameter panel, and status bar

The window is divided into four areas:

Status bar overview — all 5 rows annotated

┌─────────────────┬──────────────────────────────────────┐
│  Instance list  │       Lattice preview (3D iso)        │
│                 ├──────────────────────────────────────┤
│  [+Add] [-Rem]  │    Parameter panel  [↺ Def]           │
│  [Dup]          │    (scrollable, per-instance)         │
├─────────────────┴──────────────────────────────────────┤
│  Row 1: OSC · Preset name · Save · Reset               │
│  Row 2: Speed · BPM · Fwd/Rev · Pause · Stop · PP · Sync │
│  Row 3: Offset X Y Z · Move X Y Z                     │
│  Row 4: Rotate Pt · Yw · Rl · Zoom                    │
│  Row 5: Spin Pt · Yw · Rl · Arrange buttons           │
│  Preset bar: Cubic · Tetragonal · … · Triclinic        │
└────────────────────────────────────────────────────────┘

4. Instance list

The left panel lists all active instances. Each row shows the instance number, color dot, name, and current step count.

ControlAction
Click a rowSelect that instance; parameter panel updates
Shift + clickAdd to selection (multi-select); all selected instances share parameter edits
+ AddCreate a new instance with default settings
− RemDelete the selected instance
DupDuplicate the selected instance
Keyboard AAdd instance
Keyboard DDuplicate selected
Keyboard RReset selected instance to step 0
Keyboard Cmd+ASelect all instances at once
Warning
The maximum is 64 instances. Adding beyond this limit has no effect.

5. Lattice preview

Instance list (left) and isometric lattice preview with 8 cubic sources — selected instance highlighted

The top-right panel shows all enabled instances as colored dots in an isometric 3D projection. A unit-cube guide is drawn in the background.

InteractionEffect
Drag a dotMove the instance in the XY plane (updates Start X and Start Y)
Shift + dragMove the instance along the Z axis (updates Start Z)
Shift + click a dotAdd that instance to the multi-select set
HoverShows instance name and a highlight ring

Edges are drawn between any two instances within EDGE_DIST world units — this gives a quick visual of the lattice topology.


6. Parameter panel

The right column below the preview shows all parameters for the selected instance. Use the mouse wheel to scroll.

The panel header shows the instance number and name. A small ↺ Def button in the top-right corner of the header resets all parameters of the selected instance(s) to factory defaults (position, offsets, rate, step count, mode, rotation, scale, bounds, smoothing). The instance name and colour are preserved. If multiple instances are selected, all are reset at once.

Editing values

Every numeric field supports three ways to change its value without typing:

MethodHowNote
Scroll wheelClick the field to focus it, then scroll up/downEach key type has an appropriate step size (see table below). Hold Shift for a ×10 coarser step.
Cmd + dragHold Cmd, click the field, then drag the mouse up (increase) or down (decrease)Hold Shift additionally for a ×0.1 finer step. Changes propagate to all selected instances as a relative delta.
Direct editClick the field, type a value, press EnterPress Esc to cancel.

Scroll step sizes per field type:

FieldStep
Step Count1 (integer only)
Rotation fields1.0°
Offset fields0.005
Rate0.05
Start position0.01
Smoothing / Glide0.01

Basic parameters — everything you need for a first session:

Identity

ParameterDescription
NameLabel shown in the instance list
EnabledIf unchecked, the instance is frozen and excluded from OSC output

Position

ParameterDescription
Start X / Y / ZWorld-space origin of this instance (drag in preview to set visually)
Offset X / Y / ZStep vector — how far the source moves per step along each axis

Each step, the raw position is computed as:

position = Start + currentStep × Offset

Timing

ParameterDescription
RateSteps per second (BPM OFF) or steps per beat (BPM ON)
StepsTotal step count; determines turn-around point for Finite and Pingpong
ModeInfinite — steps forever; Finite — stops at last step; Pingpong — bounces

Motion transforms — shape the path after the basic linear motion is computed:

Rotation

Rotation pivots around the Start position using an Euler rotation matrix. Angles are in degrees.

ParameterDescription
Rot X / Y / ZEuler angles in degrees
OrderApplication order: XYZ, XZY, YXZ, YZX, ZXY, ZYX

Scale

Scales the offset vector relative to the Start position.

ParameterDescription
Scale X / Y / ZPer-axis stretch factor

Bounds

Constrains the position to a box after rotation and scale.

ParameterDescription
Bounds OnEnable bounds processing
ModeNone, Clamp (stop at edge), Wrap (teleport to opposite side), Mirror (reflect)
Min / Max X / Y / ZBounding box corners

Quantize

ParameterDescription
Space Q.Snap final position to a grid before smoothing
Time Q.Snap step advances to beat subdivisions
Grid X / Y / ZGrid cell size per axis (Space Q.)

Smoothing

An exponential-decay lerp between the stepped target position and the output:

alpha      = 1 − exp(−dt / glideTime)
currentPos = currentPos + alpha × (targetPos − currentPos)
ParameterDescription
SmoothingEnable/disable the filter
Glide TimeTime constant in seconds — 0.08 s is snappy, 0.5 s is slow

Advanced — instances influencing each other (optional for basic use):

Interaction

Parameter panel (lower sections) — Bounds, Quantize, Smoothing, and Interaction visible

Instances can influence each other’s speed and direction based on proximity.

ParameterDescription
InteractionEnable for this instance
Send AmountHow strongly this instance influences others
Receive AmountHow strongly this instance accepts incoming influence
RadiusSphere of influence in world units
Falloff ModeLinear / Inverse Square / Gaussian
Affect OffsetIncoming influence modifies the step direction
Affect RateIncoming influence modifies the step rate

7. Status bar — Row 1: OSC and Presets

Status bar — all four control rows and the eight preset buttons at the bottom

OSC

ControlDescription
Colored dotGreen = connected, red = disconnected
Host fieldIP address of the OSC target (default 127.0.0.1)
Port fieldUDP port of the OSC target (default 9001)
Connect / DisconnectOpen or close the OSC bridge
in: PORTShown when connected — the UDP port on which the Kristall listens for incoming OSC messages (always output port + 1, e.g., 9002 when the output port is 9001)

Click any field to edit, then press Enter to confirm.

Presets

ControlDescription
Name fieldType a preset name
Open the saved-preset dropdown
SaveSave all current instances under the preset name
ResetClear all instances and revert to the default Cubic preset

Presets are stored in REAPER’s ExtState (project-independent, persistent across sessions).


8. Status bar — Row 2: Playback controls

ControlDescription
Speed ×N.NNGlobal rate multiplier — scales all instance rates uniformly. Click to type a value, Enter to confirm.
BPMToggle BPM sync. Off = steps/second (absolute). On = steps/beat (follows REAPER tempo).
> Fwd / < RevGlobal direction. Fwd = all instances step forward. Rev = all instances step in reverse.
‖ PauseFreeze all motion at the current step. Click again to resume.
■ StopReset all instances to step 0 and pause immediately (motion does not resume automatically). Click ‖ Pause or restart to continue.
⇄ PPToggle global Pingpong mode — all instances bounce instead of repeating forward.
⊙ SyncReset the phase accumulator and step counter of all selected instances to 0, snapping them back in sync without affecting instances that are not selected.

BPM mode in detail

When BPM is OFF: Rate = 2 means the instance advances 2 steps every second, regardless of tempo.

When BPM is ON: Rate = 1 means 1 step per quarter note. At 120 BPM this is 2 steps/sec; at 60 BPM it is 1 step/sec. An instance with 64 steps and Rate 1 completes one cycle every 64 quarter notes (16 bars in 4/4).


9. Status bar — Row 3: Global offset and movement

Row 3 contains six scrubber sliders, all with range −2.0 to +2.0.

Offset X / Y / Z — Global translation

Shifts all instance positions uniformly after all per-instance transforms. Use this to place the entire crystal anywhere in the Ambisonics space without touching individual Start positions.

Example: Offset X = 0.5 moves all sources 0.5 units to the right.

Move X / Y / Z — Global movement direction

Adds a per-step offset to all instances on top of their own Offset X/Y/Z. Use this to make the entire crystal drift through space in a chosen direction.

Example: Move X = 0.01 adds 0.01 units per step along X to every instance — the whole crystal drifts rightward. Combine Move X = 0.007 and Move Y = 0.007 for a diagonal drift.

Warning
Offset and Move are not saved in presets — they are session-level controls intended for real-time performance.

10. Status bar — Row 4: Global rotation and zoom

Row 4 contains three rotation scrubbers and a Zoom scrubber, all applied after per-instance transforms and the global Offset.

Rotation (Pt / Yw / Rl)

Rotates the entire crystal around the world origin. Angles in degrees, range −180 to +180.

SliderAxisEffect
Pt (Pitch)XTilts the crystal forward / backward
Yw (Yaw)YSpins the crystal left / right
Rl (Roll)ZRolls the crystal clockwise / counter-clockwise

Zoom (×)

Scales the entire crystal figure uniformly around the origin — after rotation, before translation. Range 0.0–2.0, neutral at 1.0.

ValueEffect
0.0All sources collapse to the origin
1.0No change (neutral)
2.0Figure double size

Zoom is applied to the lattice preview and the live OSC output in real time. Drag the × scrubber or click it to type an exact value.

Drag any slider horizontally or click it to type a value, then press Enter to confirm.

Warning
Rotation and Zoom act on the final effective position of every instance. The lattice preview updates in real time even while playback is paused.

Rotation axes — Pt tilts forward/backward, Yw spins left/right, Rl rolls CW/CCW

Rotation can also be controlled externally — see §11 External control.


11. External control — OSC input and MIDI

OSC input

When the OSC bridge is connected (Connect clicked, status dot green), the Kristall simultaneously listens for incoming OSC messages on output port + 1 (e.g., port 9002 when the output port is 9001). The receive port is shown as in: PORT next to the Connect button.

Send any of these messages from TouchOSC, Max/MSP, OSSIA, SuperCollider, or any OSC-capable tool:

OSC addressArgumentsEffect
/kristall/pitch<float degrees>Set global Pitch (−180…+180)
/kristall/yaw<float degrees>Set global Yaw (−180…+180)
/kristall/roll<float degrees>Set global Roll (−180…+180)
/kristall/rotate<float pitch> <float yaw> <float roll>Set all three at once

The values are applied immediately and override the on-screen sliders. Clipping to −180…+180 is applied automatically.

OSC/MIDI signal flow — two input paths converging to global rotation

Example — rotate the crystal with TouchOSC:

  1. Connect in the Kristall GUI (output port 9001, target 127.0.0.1).
  2. In TouchOSC, set the OSC target to 127.0.0.1:9002.
  3. Assign a fader to /kristall/yaw, range −180 to +180.
  4. Moving the fader spins the crystal in real time.

MIDI via JSFX controller bridge

If a Kristall Controller JSFX is present on any track, the Kristall script reads its parameters automatically:

JSFX sliderParameter indexRangeEffect
slider65−180…+180°Global Pitch
slider76−180…+180°Global Yaw
slider87−180…+180°Global Roll
slider98−2…+2Offset X
slider109−2…+2Offset Y
slider1110−2…+2Offset Z
slider1211−2…+2Move X
slider1312−2…+2Move Y
slider1413−2…+2Move Z
slider15140…2Zoom ×

Map MIDI CCs to sliders 6–8 for rotation control. For automation envelopes on Offset, Move, and Zoom (sliders 9–15): right-click the JSFX in the FX chain → Param → enable the envelope for the desired slider, then draw it in the REAPER envelope lane. The sliders are read every frame as long as the JSFX is present on any track.

Automation envelope workflow — Offset X/Y/Z, Move X/Y/Z and Zoom drawn in REAPER envelope lanes

Warning
JSFX Pitch/Yaw/Roll take effect only when at least one of the three is non-zero. If all three are 0 (or the JSFX does not expose these sliders), the on-screen sliders retain their values.

12. Built-in presets (quick-select)

Preset layouts — top-view dot patterns for all 8 presets

Eight preset buttons appear at the very bottom of the window. Each clears all current instances and places new ones. Buttons 1–4 (teal) are abstract motion layouts; buttons 5–8 (amber) are crystallographic unit-cell shapes.

#PresetWhat it creates
1Cubic8 instances at the corners of a unit cube. Each corner oscillates toward the centre (0, 0, 0) and back in Pingpong mode — a “breathing cube” effect.
2TetragonalGrid with equal XY spacing and different Z spacing
3Hexagonal2D hexagonal ring tiled along Z (mirrors typical dome layouts)
4Rnd.Swarm20 instances scattered randomly within a sphere
5OrthorhombicUnit cell with three unequal orthogonal axes (α=β=γ=90°)
6RhombohedralUnit cell with equal axes and equal non-orthogonal angles (α=β=γ≠90°)
7MonoclinicUnit cell with one inclined axis (α=γ=90°, β≠90°)
8TriclinicUnit cell with no equal axes and no right angles — maximum asymmetry

13. First session walkthrough

Update
Start here. This walkthrough takes about five minutes and covers the core workflow. No prior knowledge of the parameters is needed — the Cubic preset is loaded automatically when you click Reset.

Step 1 — Apply a preset

Click Cubic in the status bar. Eight instances appear in the lattice preview, one at each corner of a cube.

Step 2 — Start motion

Set Speed to ×1.00, make sure BPM is off. The instances are moving — watch the step counters in the instance list increment.

Step 3 — Adjust direction

In the parameter panel, select instance 1. Set Offset X = 0.02, Offset Y = 0.01, Offset Z = 0. This instance now drifts diagonally in XY.

Step 4 — Bounce with Pingpong

Set Mode = Pingpong and Steps = 64. The source travels 64 steps out and bounces back. Repeat for other instances with different offsets.

Step 5 — Add global drift

Drag the Move X slider in Row 3 slightly to the right (e.g., 0.008). The entire crystal now drifts rightward while individual instances still bounce within it.

Step 6 — Rotate the crystal

Drag the Yw (Yaw) slider in Row 4 to spin the whole crystal around the vertical axis. Combine Pt and Rl for compound orientations. The lattice preview updates in real time.

Step 7 — Connect OSC

Enter your AmbiEncoder host and port in Row 1, then click Connect. Positions are streamed as OSC messages to the encoder in real time. After connecting, in: 9002 appears — this is the port for incoming rotation control (see §11).


14. Troubleshooting

Sources do not move. Check that Speed > 0 and that the instance is Enabled. In BPM mode, REAPER transport must be running.

All instances are at the same position. Apply a preset (e.g., Cubic) to distribute them, or set unique Start positions manually.

Positions drift outside the speaker array. Mode is set to Infinite with a non-zero Offset. Switch to Pingpong, or enable Bounds with Mirror mode.

Move slider changes are too coarse. Drag slowly — the slider covers the full −2 to +2 range. For fine control, click the slider to enter a numeric value, then press Enter.

OSC not connecting. Verify that the Python OSC bridge is running (python-osc required). Check host IP and port. The status dot turns green on a successful connection.

OSC not connecting on Windows. Three things to check: (1) Python must be in the system PATH — reinstall Python and tick “Add Python to PATH” if missing. (2) Windows Firewall may show a popup the first time Python opens a UDP port — click “Allow access”. (3) If the status dot turns green but no movement occurs, make sure the AmbiEncoder host IP is 127.0.0.1 for a local connection.

OSC input (rotation) has no effect. The OSC bridge must be connected first (output port active). Check that your controller is sending to output port + 1 (default: 9002), not 9001. Confirm the messages arrive with a UDP monitor (e.g., Protokol).

Stop button does not pause — it starts playing. Update to the latest script (v2.2.8) — this was a bug in early pre-release versions.


See also