embed a program in Javascript to max4live.

benjamin vyger's icon

Hello and happy new year to everyone. Cheers from Bordeaux!

I would like to integrate a javascript program into max/msp, hoping that it will work in max for live.

To launch it I use the Processing software in Javascript mode.

The goal will be to get in max4live, the phases of each of 10 balls that we see rotating around a circle.

I explain at the end the purpose of this program

Thank you very much for your attention!

gets positions (phases) of 10 balls

Here the program in Javascript file

songFilteredJavascrpitalightedVeryB.js
js

Here you can see the program made with Processing

// Simulation of coupled oscillators network in JavaScript using p5.js
let networkSize = 10;
let stepSize = 0.05;
let noiseLevel = 0.2;
let radius = 75;
let coupling = 0;

let time = 0;
let phases = [];
let naturalFrequencies = [];
let velocities = [];
let accelerations = [];
let couplingMatrix = [];

function setup() {
  createCanvas(200, 200);
  frameRate(24);
  initializeNetwork();
}

function draw() {
  background(220);
  translate(width / 2, height / 2);
  coupling = map(mouseX, 0, width, -10, 10);
  setCoupling(coupling);
   
  drawTrack();
  drawOscillators();
  drawOrderVector();
  updateNetwork();
}

//---------------------------------- library to compute phase of each oscillators

// Initialize the network of oscillators
function initializeNetwork() {
  for (let i = 0; i < networkSize; i++) {
    phases.push(i*(TWO_PI/networkSize)); /// initial phases
    naturalFrequencies.push(1); // initial frequencies
    velocities.push(0);
    accelerations.push(0);
  }

  for (let i = 0; i < networkSize; i++) {
    couplingMatrix[i] = [];
    for (let j = 0; j < networkSize; j++) {
      couplingMatrix[i][j] = coupling;
    }
  }
}

// Draw the circular track
function drawTrack() {
  noFill();
  stroke(100);
  ellipse(0, 0, 2 * radius, 2 * radius);
}

// Draw oscillators on the track
function drawOscillators() {
  for (let i = 0; i < networkSize; i++) {
    let x = radius * cos(phases[i]);
    let y = radius * sin(phases[i]);
    fill(255 / (i + 1), 0, 255, 50);
    stroke(0);
    ellipse(x, y, 30, 30);
  }
}

// Draw a line pointing to the average phase of the network
function drawOrderVector() {
  let orderVector = getOrderVector();
  let orderX = radius * orderVector.x;
  let orderY = radius * orderVector.y;
  stroke(100);
  line(0, 0, orderX, orderY);
}

// Update the network of oscillators
function updateNetwork() {
  let noiseValue = noiseLevel * noise(time); // Renamed noise to noiseValue
  let oldPhases = [...phases];
  let oldVelocities = [...velocities];

  for (let i = 0; i < networkSize; i++) {
    let k1 = stepSize * differentiate(0, i, noiseValue, oldPhases);
    let k2 = stepSize * differentiate(k1 / 2, i, noiseValue, oldPhases);
    let k3 = stepSize * differentiate(k2 / 2, i, noiseValue, oldPhases);
    let k4 = stepSize * differentiate(k3, i, noiseValue, oldPhases);

    phases[i] = phases[i] + (k1 + 2 * k2 + 2 * k3 + k4) / 6;
    velocities[i] = (phases[i] - oldPhases[i]) / stepSize;
    accelerations[i] = (velocities[i] - oldVelocities[i]) / stepSize;

    // Keep phases within 0 to TWO_PI
    phases[i] = (phases[i] + TWO_PI) % TWO_PI;
  }

  time += stepSize;
}

// Differentiate the phase of an oscillator
function differentiate(increment, i, noiseValue, oldPhases) {
  let derivative = naturalFrequencies[i] + noiseValue;
  for (let j = 0; j < networkSize; j++) {
    derivative += (couplingMatrix[i][j] / networkSize) * sin(oldPhases[j] - (oldPhases[i] + increment));
  }
  return derivative;
}

// Set the coupling strength within the network
function setCoupling(couplingStrength) {
  for (let i = 0; i < networkSize; i++) {
    for (let j = 0; j < networkSize; j++) {
      couplingMatrix[i][j] = couplingStrength;
    }
  }
}

// Calculate the order vector of the network
function getOrderVector() {
  let x = 0;
  let y = 0;
  for (let i = 0; i < networkSize; i++) {
    x += cos(phases[i]);
    y += sin(phases[i]);
  }
  x /= networkSize;
  y /= networkSize;
  return createVector(x, y);
}
//-------------------------------------------- END of library

// Handle key press events for additional features
function keyPressed() {
  switch (key) {
    case '1':
      invertNaturalFrequencies();
      break;
    case '2':
      applyProportionalFrequencies();
      break;
    case '3':
      applySamePhaseOffset();
      mouseX = width/2; 
      break;
    case '4':
      applySamePhaseOffset(); 
      applyFixedFrequencies(1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
      break;
    case '5':
      applySamePhaseOffset();
      applyFixedFrequencies(1, 1, 1, 1, 1, 0, 1, 1, 1, 1);
      break;
    case '6':
      for (let i = 0; i < networkSize; i++) {
      phases[i] += TWO_PI/networkSize;
      }
      break;
    case '7':
      mouseX = width/2; 
      for (let i = 0; i < networkSize; i++) {
        phases[i] += TWO_PI/networkSize;
      }
      applyFixedFrequencies(0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
      break;  
    default:
     console.log('Unrecognized key');
  }
}
function applySamePhaseOffset() {
       for (let i = 0; i < networkSize; i++) {
        phases[i] = i*(TWO_PI/networkSize);
      }
}

function  invertNaturalFrequencies() {
      for (let i = 0; i < networkSize; i++) {
    naturalFrequencies[i] = -naturalFrequencies[i];
  }
}

function applyProportionalFrequencies() {
  for (let i = 0; i < networkSize; i++) {
    naturalFrequencies[i] = i / 10;
  }
}

function applyFixedFrequencies(...freqs) {
  for (let i = 0; i < freqs.length && i < networkSize; i++) {
    naturalFrequencies[i] = freqs[i];
  }
}
______ index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <!-- PLEASE NO CHANGES BELOW THIS LINE (UNTIL I SAY SO) -->
  <script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
  <script language="javascript" type="text/javascript" src="songFilteredJavascrpitalightedVery.js"></script>
  <!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN -->

  <style>
    body {
      padding: 0;
      margin: 0;
    }
  </style>
</head>

<body>
</body>
</html>

This program allows you to control the phase and speed (called frequency) of 10 coupled oscillators. (You have to imagine 10 metronomes on a skateboard…)

The library calculating the average phases of the oscillators is between line 32 and 133.

To make a long story short this program calculates the average phase of ten "balls" rotating around a circle.

These balls are coupled more or less strongly (by the movement of the mouse in X).

I did several cases from 1 to 7 which allow me to try different phase and frequency configurations.

TFL's icon

Hi from not too far from you, a bit more in the south!

If you want to embed some Processsing javascript code in Max, you need to run it as a webpage inside of [jweb]. You can find an example here. I believe it can work in M4L.

But:

  • [jweb] is a small chromium-based browser in its own, so really not the most efficient way to display things. Best would be to convert the Processing code into Max-compatible API like MGraphics or Sketch that you can both use in [v8ui].

  • It seems that you want to use that simulation code for driving audio. Depending on how snappy you want your simulation to be, and especially if you plan to allow for automatize to parameters of the simulation, I would suggest to not do the simulation in javascript at all but with gen, jitter, or even gen~. I would use javascript only for display purpose, not driving the simulation itself

So yeah, maybe you can try with the processing code in jweb, and if it works for you then fine. But you'll get smoother results and better performance if you perform the simulation in plain Max (best would be using gen~ ), and do the visualization in v8ui.

benjamin vyger's icon

Hi Hi,

Thanks for your help!

When I looked at your link, it seemed too complicated for me

So I asked chatgpt for help and after 6 hours of effort here is the result

coupled phases of oscilators.

It looks nicer if you set the LFO between 0.2 and 0.3 hz and switch between initialize and setPhases

I have many other features that I will add as I go along.

But I have a lot of things to do, so I may ask for your help a few more times.

The next step will be to modulate the torque according to a sound signal coming from Live. (instead of LFO)

Thanks for your next answers, it is motivating to continue working.

Cheers. Benjamin

Max Patch
Copy patch and select New From Clipboard in Max.