Device Details


Overview

Name | Version: Kasm Emanator 1.12
Author: kevleyski
Device Type: MIDI Effect
Description: Emanator, generate MIDI note/velocity sequences as complex as you like!

It was built with Rust/WebAssembly source code available to you here:
https://maxforlive.com/library/device/12909/kasm-ableton-wasm-rust-source-code-example

!! NOTE: you need the latest Ableton 12.2 for this as it uses the new V8 jsinterp from Max9 !!

Kasm is WebAssembly so you can develop and test things in web browser too, try it out here:
https://pyrmontbrewery.com/kasm


For those interested, this is what the Emanator Rust code (Ableton Live and Web version uses identical WebAssembly)...


|____emanators
| |____spatial.rs
| |____melodic.rs
| |____loopcounterpoint.rs
| |____experimental.rs
| |____responsorial.rs
| |____mod.rs
| |____mathematical.rs
| |____harmonic.rs
| |____drumpattern.rs
| |____rhythmic.rs
| |____progressions.rs



pub fn rhythmic_morse_code(
note: i32,
offset: i32,
velocity: i32,
enc1_velocity_offset: i32,
enc2_intensity: i32,
_time_step: i32,
_time_bar: i32,
) -> i32 {
let root_note = (note + offset).max(0).min(127);
let base_velocity = (velocity + (enc1_velocity_offset / 10)).max(30).min(127);
let intensity_factor = enc2_intensity.max(1).min(127) as f32 / 32.0;

// Morse code patterns for different notes
let morse_alphabet = [
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---",
"-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-",
"..-", "...-", ".--", "-..-", "-.--", "--..",
];

let morse_index = ((root_note + enc1_velocity_offset) as usize) % morse_alphabet.len();
let morse_pattern = morse_alphabet[morse_index];

let dot_duration = (120.0 * intensity_factor) as i32;
let dash_duration = (360.0 * intensity_factor) as i32;
let element_gap = (120.0 * intensity_factor) as i32;

let mut current_delay = 0;
let total_elements = morse_pattern.len();

for (i, morse_char) in morse_pattern.chars().enumerate() {
let pan_angle = (i as f32 / total_elements as f32) * 2.0 * std::f32::consts::PI;
let pan_position = ((pan_angle.cos() + 1.0) * 63.5) as i32;

let (duration, pitch_offset) = match morse_char {
'.' => (dot_duration, 0),
'-' => (dash_duration, 12),
_ => continue,
};

let morse_note = (root_note + pitch_offset).max(0).min(127);
let morse_velocity = (base_velocity as f32 * (0.8 + 0.4 * intensity_factor)) as i32;

send_note(
morse_note,
morse_velocity.max(30).min(127),
current_delay,
duration,
pan_position,
);

current_delay += duration + element_gap;
}

root_note
}


pub fn spatial_3d_positioning(
note: i32,
offset: i32,
velocity: i32,
x_movement: i32,
y_movement: i32,
_time_step: i32,
_time_bar: i32,
) -> i32 {
let root_note = (note + offset).max(0).min(127);
let x_factor = x_movement.max(0).min(127) as f64 / 127.0;
let y_factor = y_movement.max(0).min(127) as f64 / 127.0;

let num_positions = 12;
let position_duration = 300;

for pos in 0..num_positions {
let t = pos as f64 / num_positions as f64;

// Simulate 3D movement
let x = (t * 2.0 * std::f64::consts::PI * x_factor).sin();
let y = (t * 2.0 * std::f64::consts::PI * y_factor).cos();
let z = (t * std::f64::consts::PI).sin(); // Height simulation

// Convert 3D position to stereo pan
let pan_position = ((x + 1.0) / 2.0 * 127.0) as i32;

// Simulate distance with velocity and filter
let distance = (x * x + y * y + z * z).sqrt();
let distance_factor = (2.0 - distance).max(0.1).min(2.0);
let pos_velocity = (velocity as f64 * distance_factor * 0.7) as i32;

// Height affects pitch
let pitch_offset = (z * 12.0) as i32;
let spatial_note = (root_note + pitch_offset).max(0).min(127);

let delay = pos * position_duration;
let duration = (position_duration as f64 * distance_factor) as i32;

send_note(
spatial_note,
pos_velocity.max(20).min(127),
delay,
duration,
pan_position,
);

// Add filter automation based on distance
let filter_value = (40.0 + distance_factor * 60.0) as i32;
send_cc(74, filter_value.max(20).min(127), delay);
}

root_note
}


pub fn experimental_multidimensional_markov(
note: i32,
offset: i32,
velocity: i32,
enc1_intensity: i32,
enc2_complexity: i32,
_time_step: i32,
_time_bar: i32,
) -> i32 {
let root_note = (note + offset).max(0).min(127);
let intensity_factor = (enc1_intensity.max(50).min(127) as f64 + 50.0) / 127.0;
let complexity_factor = (enc2_complexity.max(40).min(127) as f64 + 40.0) / 127.0;

let pitch_classes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 17, 19, 21, 24];
let rhythm_classes = [300, 350, 400, 450, 500, 600, 750];
let harmonic_contexts = ["major", "minor", "diminished", "augmented", "sus4", "sus2"];

let sequence_length = (20.0 + complexity_factor * 16.0) as usize;

let mut pitch_state = 0;
let mut rhythm_state = 2;
let harmonic_state = 0;
let mut phrase_position = 0;

for step in 0..sequence_length {
let pitch_transition_weights = match harmonic_contexts[harmonic_state] {
"major" => [0.2, 0.05, 0.15, 0.05, 0.15, 0.1, 0.05, 0.15, 0.05, 0.03, 0.02, 0.02, 0.08, 0.05, 0.03, 0.01, 0.01, 0.01, 0.02],
"minor" => [0.15, 0.05, 0.1, 0.15, 0.1, 0.15, 0.05, 0.1, 0.05, 0.05, 0.03, 0.02, 0.08, 0.05, 0.03, 0.01, 0.01, 0.01, 0.02],
_ => [0.05; 19],
};



...

full source code is here https://maxforlive.com/library/device/12909/kasm-rust-ableton-wasm-source-code


Details

Live Version Used: 12.2
Max Version Used: 9.0
Date Added: Jun 28 2025 07:42:35
Date Last Updated: Jul 28 2025 12:48:06
Downloads: 180
License: None
Average Rating

Log in to rate this device

-n/a-

Files

Device File: Kasm Emanator.amxd
 


Comments

This is sweet, thanks for sharing it!  Might have to look into rust and the patch you posted - sounds kind of fun!  Thanks again!

Login to comment on this device.

Browse the full library