How to get two instances of "the same" javascript object in separate top level patchers communicating
IDK if this information is already documented somewhere, or if the same behavior can be implemented more simply and easily. This is just how I figured out I could implement certain behavior I wanted for a project. Since I struggled so mightily acquiring the details, I thought I would share my solution. I'd also like to know about any better (safer, more flexible maybe) way(s) anyone else has for implementing this kind of behavior. Please reply and share.
DL and save this txt file to a local folder, change the extension from .txt to .js.
Save (your "new from clipboard" copy of) the below compressed patch to the same folder as the patchers.js file (saveAs "patcherA.maxpat" maybe)
Save (your "new from clipboard" copy of) the below compressed patch to the same folder as the "patcherA" Patch and the patchers.js files (saveAs "patcherB.maxpat" maybe)
Close both "new" Patchers, then reopen one then the other locally from Max File menu. (so they can find the patchers.js file when they load)
PatcherA: (PatcherB is functionally identical to A except B loadbangs"initB")

The two top level patchers each contain a Max js object and each js object is an instance of the code found in the patchers.js file and the two "siloed" instances can still call a method "in" the other instance (after both patches are open).
The "reportOut" messages you pass into "patcherA's" js object appear at "patcherB's" js outlet, and vice versa.
This demo verbosely posts to the max console, but it's for demonstration purposes. Comment out the informational console posts for actual use of the code.
TLDR: Use "introspection" on the Max environment to locate the "Maxobj" (js) objects of concern by "scripting name", then call javascript functions on the "scripting name" objects using the js introspection syntax.
var fnA = "fromA";
if (maxObjB && maxObjB.valid) {
maxObjB.js[fnA](msg);
post("reportOut: *msg sent!"); post();Anyone else think this is useful info?
Jon
That's nice! I never realized you could traverse patcher windows using `max.frontpatcher.wind.next`.
You can probably simplify your code and make so it can send a message to any given object with a specific scripting name (not only js objects).
To search this object, you could iterate through the windows as you currently do, but then you could use max_patcher.applydeepif()to get all objects with the given scripting name.
...but for simpler code and at the cost of an additional object, I would probably just use messnamed() combined with [receive]. No need to seek for the object(s) at all, since send/receive names are global and can be reached from anywhere.
I had to try it. The code:
let target_name = null;
let target_obj = null;
function send_to() {
const args = arrayfromargs(arguments);
const name_candidate = args.shift();
if (this.box.varname == name_candidate) return; // Avoid sending messages to itself!
if (args.length) {
if (target_name != name_candidate) { // Avoid looking for the same object if it has already been found
target_name = name_candidate;
target_obj = null
let patch = max.frontpatcher;
let keep_looking = true;
while (patch && !target_obj) {
patch.applydeepif(set_target_object, is_target);
const next_window = patch.wind.next;
patch = next_window ? next_window.assoc : null;
}
if (!target_obj) target_name = null;
} else {
if (target_obj) {
post("Sending to ", target_name, ":", args, '\n');
target_obj.message(...args);
}
}
}
}
function is_target(obj) {
if (obj.varname == target_name) {
post("Object found in patcher", obj.patcher.name, '\n');
return true
}
return false
}
function set_target_object(obj) {
target_obj = obj;
}
// Code to receive messages from other source
function anything() {
let args = arrayfromargs(messagename, arguments);
outlet(0, ...args, '\n');
}It is agnostic to the patcher and objects scripting name: the same code can be used in any number of v8/v8ui without the need of changing the code.
It will search only for a scripting name and will send only to the last applydeepif match of the first patcher window with a candidate (in case you have multiple objects with the same scripting name), but you could easily add filters in is_target() to make sure to target only some specific patch or object.
You could also change the code structure to make the message to be sent to every object with the given scripting name. Basically by storing all matches in a target_objs array.
Example with patch A:
and patch B:
The code is embedded in each patcher, and no need to save them as "New from clipboard" already make them top-level patchers.
But again, I don't really see the true benefit of this compared to messnamed() + [receive], or [pattrforward] which can talk to any inlet of any named object (not only pattrized objects) as long as you can identify them with a [pattrmarker] in the same patch (or parent patch).
Thanks, and thanks again.
Your code example is way more general purpose than is required in my project, and I agree handling message passing by iterating through all (opened) patcher objects to find particular message recipient objects is super inefficient. Brother BigO is a big drag-anchor.
I've been sticking to the legacy js coding environment because my sequencer project has been underway since well before v8 was even available, and I don't plan a major refactoring of all that legacy code before the v8 js engine is running the legacy js show under the hood anyway. (Has that already happened?)
I shied away from trying applydeepif() because that JS API documentation doesn't mention js objects, only sub-patcher objects. I didn't really know these details originally, but now I think I can say, I was concerned about "only" recursing into sub-patchers because: Maxobj is the base/general object type that patchers maintain a collection of, and Maxobj instances have a "special" js property that is only not-undefined if the instance "is" a js object as well as a subpatcher() method that returns undefined unless the object "is" a patcher object. So, I was already in the weeds and I didn't want to try getting out by planting more seeds of doubt. (if applydeepif didn't handle the js property properly, I would have had no way to tell the difference between that issue I can't fix and any issue I could fix - and the documentation was silent on the matter), best not to introduce new variables in the middle of problem solving if it can be avoided. Your code is a great functional example of using applydeepif though, regardless of its suitability in my specific case right now, which is getting two valid js object references so the two (siloed) js instances can directly message each other (can each call methods on the other as needed).
I broke out of working on my "real" project to test-build the little experimental patches and javascript I shared above without all the other project code in my way. Referencing the js property of the target Maxobj maxObjB.js[fnA](msg); instead of messaging the Maxobj directly maxObjB[fnA](msg); turned out to be the key that finally unlocked the door blocking progress to me in this case.
There is nothing v8-specific in this code beside some syntaxes that can be adapted. This should work in js:
var target_name = null;
var target_obj = null;
function send_to() {
var args = arrayfromargs(arguments);
var name_candidate = args.shift();
if (this.box.varname == name_candidate) return; // Avoid sending messages to itself!
if (args.length) {
if (target_name != name_candidate) { // Avoid looking for the same object if it has already been found
target_name = name_candidate;
target_obj = null
var patch = max.frontpatcher;
var keep_looking = true;
while (patch && !target_obj) {
patch.applydeepif(set_target_object, is_target);
var next_window = patch.wind.next;
patch = next_window ? next_window.assoc : null;
}
if (!target_obj) target_name = null;
} else {
if (target_obj) {
post("Sending to ", target_name, ":", args, '\n');
target_obj.message(...args);
}
}
}
}
function is_target(obj) {
if (obj.varname == target_name) {
post("Object found in patcher", obj.patcher.name, '\n');
return true
}
return false
}
function set_target_object(obj) {
target_obj = obj;
}
// Code to receive messages from other source
function anything() {
var args = arrayfromargs(messagename, arguments);
outlet(0, args, '\n');
}And there is nothing that prevents you here to call specific methods defined in your js. Right now function anything() catches every message coming in, but if you defined, say, a function my_specific_method() in one of your [js] with my_js as a scripting name, you could call that method too, with whichever arguments you want. The correct message syntax would be: send_to my_js my_specific_method whichever arguments to send to any [js] with the code above.
Thank you. I'm saving both examples for future reference.
After implementing updated code back in my project, it turns out your first inclination was even more accurate than I first realized. As is not rarely par for the course for me when coding, I had been misinterpreting the console print debugging information I had been receiving because the information was garbled by at least 3 simultaneous bugs in my code. I had been playing whack-a-mole and getting frustrated, so I jumped out of that loop and solved one problem in isolation. Now that the project bugs are gone (for now) and the added behavior is implemented, I can see for example, there was nothing unique about my project (except bugged code) invalidating my attempt(s) at using the messnamed() + Receive object technique you first mentioned, and I'd bet big money that (Max internally) searching one global list of Receive objects is way more efficient than (any javascript code) traversing maxobj objects in open patcher windows to find specific (known) recipient objects.
Good learning experience all around...