<?xml version='1.0' encoding='UTF-8'?>

<?xml-stylesheet href="./_c74_tut.xsl" type="text/xsl"?>

<chapter name="Max JS Tutorial 2: JavaScript Scripting">

<setdocpatch name="02jJavascriptScripting" patch="02jJavascriptScripting.maxpat"/>
<previous name="javascriptchapter01">Basic JavaScripting</previous>
<next name="javascriptchapter03">Tasks, Arguments and Globals</next>
<parent name="00_maxindex">Max Tutorials</parent>

<indexinfo category="JavaScript" title="JavaScript Scripting">Using JavaScript to create patcher elements</indexinfo>

<h1>JavaScript Tutorial 2: JavaScript Scripting</h1>

<h2>Introduction</h2>

<p>With the Max <o>js</o> object, it’s possible to use JavaScript code to perform patcher scripting, where you can create Max objects in a patcher dynamically, setting their properties, sending them messages, and making connections between them. JavaScript allows you to use procedural code to generate patcher elements in ways that may be more difficult to do through messages to the <o>thispatcher</o> object (the other way to automatically create Max objects in a patcher). This Tutorial covers how to create and delete objects and connections in a Max patch through custom methods written in JavaScript, as well as to show how to use methods to handle custom messages coming from the patcher.</p>

<p>To open the tutorial patch, click on the <b>Open Tutorial</b> button in the upper right-hand corner of the documentation window.</p>

<h2>Patcher Scripting with JavaScript</h2>

<p>When you initially open the tutorial patch, you will see a largely empty patcher with a <o>js</o> object in the lower part of the patcher window. The <o>js</o> object has loaded a JavaScript source file called ‘autosurface.js’, which is located in the same folder as the Tutorial patch.</p>

<p>The <o>js</o> object is configured to send numbers to a MIDI output device (using the <o>makenote</o> and <o>noteout</o> objects). It also has a right outlet sending values to the right inlet of the <o>pack</o> object driving messages to a <o>multislider</o> object. In addition, our <o>js</o> object has a number of objects connected to its inlet. A <o>metro</o> object is connected to our <o>js</o> object, as are two <o>message</o> boxes that will send the messages <m>sliders $1</m> and <m>reverse $1</m>, where <m>$1</m> in each case is the value present in the <o>number</o> box connected to them.</p>

<p>From the patch layout, we can infer that the JavaScript code in our <o>js</o> object should have at least three functions, for <m>bang</m>, <m>sliders</m>, and <m>reverse</m>. It actually has one more, which will become apparent when we use the patch.</p>

<h2>Patch Auto-Generation</h2>

<p>Select the <o>number</o> box attached to the message box containing the <m>sliders $1</m> message. Type in or scroll to the number <m>5</m>, and watch what happens. Change the value in the <o>number</o> box. Try setting it to a large number (like <m>50</m>).</p>

<p>Set it to <m>0</m>, and see what happens.</p>

<p>In response to our <m>sliders $</m>1 message, our <o>js</o> object dynamically <i>creates</i> Max objects and connections through <i>scripting</i>. It creates pairs of <o>ctlin</o> and <o>slider</o> objects to match the number of sliders you request through the message to the <o>js</o> object. Furthermore, it creates a <o>funnel</o> object with the appropriate number of inlets for the <o>slider</o> objects and makes the appropriate connections between them. The <o>funnel</o> object is then connected to our <o>js</o> object, allowing the values generated by the sliders to be used by our JavaScript code as well.</p>

<p>As you create sliders, note that the <o>ctlin</o> objects are automatically numbered to listen to incrementing MIDI controller numbers. As a result, a MIDI control surface that sends MIDI continuous control values on multiple controller numbers will send values to independent <o>slider</o> objects. Also, note than when you decrement the number of sliders, the excess objects will disappear (actually, everything disappears and is recreated again). If you set the number of sliders to <m>0</m>, all the script-created objects (including the <o>funnel</o>) will be deleted from the patch.</p>

<p>Set the number of sliders to something modest, such as <m>5</m>. Change the values in the <o>slider</o> objects, either by clicking on them or by using a MIDI controller input. Turn on the <o>metro</o> object by clicking the <o>toggle</o> attached to it. The values in the <o>slider</o> objects should come out of the <o>js</o> object in turn, creating a sequence of MIDI notes.  Double-click the <o>noteout</o> object to select a valid MIDI synthesizer, and you should hear them.</p>

<p>The <o>multislider</o> object to the right of the patch will give you a running display of the current note out of our sequencer, set at its appropriate position in the sequence.</p>

<p>Click the <o>toggle</o> attached to the <o>message</o> box containing the message <m>reverse $1</m>. Note that the order in which the <o>slider</o> values are sequenced is now backwards. Our <o>multislider</o> display runs backwards as well.</p>

<p>In brief, our <o>js</o> object dynamically creates a scalable MIDI control surface (with <o>ctlin</o> and <o>slider</o> objects), and uses those objects’ values to create a simple MIDI <i>step sequencer</i>. The number of sliders created by our JavaScript code determines the length of the sequence.</p>

<p>Turn off both <o>toggle</o> objects, stopping the sequence and putting the sequencer transport back into ‘forward’ mode. Let’s look at the code for our <o>js</o> object.</p>

<h2>The Global Block: Arrays and Maxobjs</h2>

<p>Double-click the <o>js</o> object in the Tutorial patch. The code for ‘autosurface.js’ should appear. At the top of the code should be the familiar comment block, explaining what the script does. Below that we should see our global code statements:</p>
<p>
<pre><code language="javascript">// inlets and outlets
inlets = 1;
outlets = 2;
// global variables and arrays
var numsliders = 0;
var seqcounter = 0;
var thereverse = 0;
var thevalues = new Array(128);
// Maxobj variables for scripting
var controlin = new Array(128);
var thesliders = new Array(128);
var thefunnel;</code></pre>
</p>

<p>As we saw in the previous tutorial, our <i>inlets</i> and <i>outlets</i> at the top of the code tell <o>js</o> how many inlets and outlets we want in our object. </p>

<p>The following block of code defines some variables that our JavaScript code will need to use globally. These variables include:
   <m>numsliders</m>: Stores how many ‘sliders’ (ctlin and slider pairs) we have in our patch. This is set by the <m>sliders</m> message to our <o>js</o> object.
   <m>seqcounter</m>: Stores the current point in our sequence. This is driven by the <o>metro</o> object in our patch, and therefore is changed by a <m>bang()</m> method in our code.
   <m>thereverse</m>: Sets whether or not our sequencer is running backwards. This is set by the <m>reverse</m> message to our <o>js</o> object.
   <m>thevalues</m>: An <i>array</i> (see below) of values reflecting the state of the <o>slider</o> objects in our patch. The <o>funnel</o> object in our patch sets these values by sending lists to our object.   </p>

<p>The new <m>Array()</m> constructor creates arrays in JavaScript. The array variable <m>thevalues</m>, above, has 128 elements, which are accessed by bracket notation following the array name, e.g.:</p>
<p>
<pre><code language="javascript">k = thevalues[5];</code></pre>
</p>
<p>will set the variable <m>k</m> to the value of the sixth element (starting from <m>0</m>) of the array <m>thevalues</m>.</p>
<p>
<pre><code language="javascript">thevalues[n] = 55;</code></pre>
</p>
<p>will set the element <m>n</m> of the our array <m>thevalues</m> to <m>55</m>.</p>

<p>Note that JavaScript treats Arrays as objects, so that:</p>
<p>
<pre><code language="javascript">k = thevalues.length;</code></pre>
</p>
<p>will set the variable <m>k</m> to the <i>number of elements</i> in the array <m>thevalues</m>. For more information on this, consult any good JavaScript reference.</p>

<p>After our variable declarations, we have variables that we will use to reference dynamically created objects in our Max patch. These variable names are used internally in our JavaScript code so that we can create, connect, delete, and modify objects all through properties to these objects. Objects in <o>js</o> that refer to Max objects in a patcher are referred to as <i>Maxobjs</i>. We have the following Maxobj variables in our script:
   <m>controlin</m>: An array of Maxobjs that refer to the <o>ctlin</o> objects in our patch.
   <m>thesliders</m>: An array of Maxobjs that refer to the <o>slider</o> objects in our patch.
   <m>thefunnel</m>: A Maxobj which references the <o>funnel</o> object in our patch.</p>

<p>Note that there is no difference in JavaScript variable declaration with relation to the <i>type</i> of value that the variable stores; integers, floats, strings, and objects are all considered equivalent when declaring a variable. Similarly, arrays are defined simply to refer to quantity of information, rather than what type of information will be stored in them. Similarly, JavaScript will correctly type variables following a calculation, e.g.:</p>
<p><pre><code language="javascript">x = 4/2;</code></pre></p>
<p>will set the variable <m>x</m> to <m>2</m> (an integer), whereas:</p>
<p><pre><code language="javascript">x = 3/2;</code></pre></p>
<p>will set the variable <m>x</m> to <m>1.5</m> (a floating-point value). Variables can switch types dynamically throughout their existence. This use of untyped variables only exists within the JavaScript environment, however, which is why we still need independent methods (<m>msg_int()</m> and <m>msg_float()</m>) to deal with differently typed numbers coming in from Max.</p>

<p>We will use various properties of the Maxobj object class to perform our scripting, all of which is accomplished by a single function: our <m>sliders()</m> method.</p>

<h2>Arguments, Agreements…</h2>

<p>Our <o>js</o> object responds to the <m>sliders</m> message via a method contained in the <m>sliders()</m> function (remember that the function name typically matches the message you want to trigger that function). Examine the code for the <m>sliders()</m> function. The comments at the top of each section explain what’s happening at each step in the process:</p>

<p>
<pre><code language="javascript">// sliders -- generates and binds the sliders in the max patch
function sliders(val)
{
   if(arguments.length) // bail if no arguments
   {
      // parse arguments
      a = arguments[0];
      // safety check for number of sliders
      if(a&lt;0) a = 0; // too few sliders, set to 0
      if(a&gt;128) a = 128; // too many sliders, set to 128
      // out with the old...
      if(numsliders) this.patcher.remove(thefunnel); // if we've done this before, get rid of the <o>funnel</o>
      for(i=0; i&lt; numsliders;i++) // get rid of the <o>ctlin</o> and <o>slider</o> objects using the old number of sliders
      {
         this.patcher.remove(controlin[i]);
         this.patcher.remove(thesliders[i]);
      }

      // ...in with the new
      numsliders = a; // update our global number of sliders to the new value
      if(numsliders) thefunnel = this.patcher.newdefault(300, 300, "<o>funnel</o>", a); // make the <o>funnel</o>
      for(var k=0;k&lt;a;k++) // create the new ctlin and uslider objects, connect them to one another and to the funnel
      {
         controlin[k] = this.patcher.newdefault(300+(k*100), 50, "<o>ctlin</o>", k+1);
         thesliders[k] = this.patcher.newdefault(300+(k*100), 100, "<o>uslider</o>");
         this.patcher.connect(controlin[k], 0, thesliders[k], 0);
         this.patcher.connect(thesliders[k], 0, thefunnel, k);
      }

      // connect new objects to this <o>js</o> object's inlet
      ourself = this.box; // assign a Maxobj to our <o>js</o> object
      if (numsliders) this.patcher.connect(thefunnel, 0, ourself, 0); // connect the <o>funnel</o> to us
   }
   else // complain about arguments
   {
      post("sliders message needs arguments");
      post();
   }
}</code></pre>
</p>

<p>In pseudo-code, our <m>sliders()</m> function performs the following steps:
Check to see if the arguments for the sliders method are valid.
If true…
   Make sure the number of sliders requested are in a reasonable range (<m>0</m>-<m>128</m>)
   Delete any objects previously created by our <o>js</o> object.
   Make the new objects and connect them to one another.
   Find our <o>js</o> object (see below) and connect our new <o>funnel</o> to it.
If false…
   Post an error message in the Max Console and exit the function.   </p>

<p>Our JavaScript code takes advantage of two important features of procedural programming, namely conditional statements (if…else…) and iteration (for() loops). If you’ve used another programming language such as C or Java, you should find these constructions familiar. A JavaScript reference will help you with the specifics.</p>

<p>One of the first things we do in our <m>sliders()</m> function is check to see what the arguments were to the sliders message sent in from the patcher. We do this by checking the <i>arguments</i> property of the function itself, e.g.:</p>

<p>
<pre><code language="javascript">if(arguments.length) {
// some code here
}</code></pre>
</p>

<p>will execute the code between the braces only if there are a non-zero number of arguments to the message that called the function. Otherwise, that part of the code will be ignored. Similarly, you can access the arguments by number as an array:</p>
<p>
<pre><code language="javascript">a = arguments[0];</code></pre>
</p>

<p>will assign the variable <m>a</m> to the value stored in the first argument of the message. In our case, this refers to the number of sliders we want to create.</p>

<h2>Object Creation and Maintenance</h2>

<p>From the perspective of using <o>js</o> for object creation in Max, the Maxobj class allows us to use our object variables to create, connect, and destroy objects. This is done by first accessing the <i>Patcher</i> object, which is a JavaScript representation of our Max patch. The statement:</p>
<p>
<pre><code language="javascript">this.patcher.remove(thefunnel)</code></pre>
</p>

<p>tells <o>js</o> to find a Maxobj called <m>thefunnel</m> in the Patcher called <m>this</m> (which is always the patcher containing the <o>js</o> object) and delete it. The ‘this’ in the statement is actually optional, but it’s worth noting that you can use JavaScript to control objects in patches other than the one in which the <o>js</o> object resides.</p>

<p>To create an object, we assign a variable to a new Maxobj created by the Patcher:</p>
<p>
<pre><code language="javascript">thefunnel = this.patcher.newdefault(300, 300, "<o>funnel</o>", a);</code></pre>
</p>

<p>In this case, the Maxobj <m>thefunnel</m> is created to be a default object at coordinates <m>300</m> by <m>300</m> on the patcher window. The object’s class is set to <o>funnel</o>, with the object’s arguments set to whatever is contained in the variable <m>a</m>.</p>

<p>Note: the <m>newdefault()</m> method to the Patcher object creates a new object just as if you had created it manually from the palette or patcher contextual menu. This simplifies scripting substantially. If you wish to specify all the object parameters (object width, flags, etc.) you can use the <m>newobject()</m> method instead.</p>

<p>Connections are made by taking two Maxobjs and linking them using the <m>connect()</m> method to a Patcher object, e.g.:</p>
<p>
<pre><code language="javascript">this.patcher.connect(thesliders[5], 0, thefunnel, 5)</code></pre>
</p>

<p>will connect the leftmost (0) outlet of the sixth Maxobj in the array <m>thesliders</m> to the sixth inlet of the Maxobject <m>thefunnel</m>. Remember that numbering starts at <m>0</m> for both arrays and inlet/outlet numbers.</p>

<p>We use iteration and arrays to create multiple objects at once, for example:</p>
<p>
<pre><code language="javascript">for(k=0;k&lt;8;k++)
{
   controlin[k] = this.patcher.newdefault(300+(k*100), 50, "<o>ctlin</o>", k+1);
   thesliders[k] = this.patcher.newdefault(300+(k*100), 100, "<o>slider</o>");
   this.patcher.connect(controlin[k], 0, thesliders[k], 0);
   this.patcher.connect(thesliders[k], 0, thefunnel, k);
}</code></pre>
</p>

<p>will automatically generate <m>8</m> <o>ctlin</o> and <o>slider</o> objects spaced <m>50</m> pixels apart on the patcher window (starting at horizontal coordinate <m>300</m>), connect them to one another, and then connect them to the <o>funnel</o> object referenced by <m>thefunnel</m>. Note that the variable <m>k</m> in our JavaScript code is never declared, since we only use it as a local variable (in the <m>sliders()</m> function) and re-initialize it every time that function is called. In our actual JavaScript code in the Tutorial patch, the number <m>8</m> is replaced by the local variable <m>a</m>, which represents the number of sliders we want to create.</p>

<h2>Finding Ourself in All of This</h2>

<p>One important thing we accomplish in our <m>sliders()</m> method is the connection of the JavaScript-created <o>funnel</o> object to our <o>js</o> object’s inlet. However, our <o>js</o> object was created by hand, not by our JavaScript code (this would be impossible, if you think about it). How do we bind a Maxobj to an object that was created independently of a JavaScript program?</p>
<p>
<pre><code language="javascript">ourself = this.box; // assign a Maxobj to our <o>js</o> object</code></pre>
</p>

<p>The ‘box’ property of our patcher returns a Maxobj referring to our <o>js</o> object itself! We then take the variable ourself and assign it to our <o>js</o> object. This allows us to make connections to the object containing our JavaScript code.</p>

<p>We then connect our <o>funnel</o> object to our <o>js</o> object using our newly assigned Maxobj ourself:</p>
<p>
<pre><code language="javascript">this.patcher.connect(thefunnel, 0, ourself, 0);</code></pre>
</p>

<h2>Other Methods</h2>

<p>The <o>js</o> object in this Tutorial doesn’t just create and connect a MIDI control surface; it also reacts to messages from the control surface as well as other messages from the Max patcher.  Open up the source code for the <o>js</o> object in the Tutorial patch again, and look for the function called <m>list()</m>:</p>
<p>
<pre><code language="javascript">// list -- read from the created <o>funnel</o> object
function list(val)
{
   if(arguments.length==2)
   {
      thevalues[arguments[0]] = arguments[1];
   }
}</code></pre>
</p>

<p>As with our <m>sliders()</m> function, our <m>list()</m> function first checks out how many values we’ve sent in from Max, e.g.:</p>
<p>
<pre><code language="javascript">if(arguments.length==2) {}</code></pre>
</p>

<p>The <o>funnel</o> object puts out a list corresponding to the number of the inlet receiving the value followed by the value received. For example, the number <m>55</m> arriving at the second inlet (which is really inlet number 1) will trigger the list <m>1 55</m> from the <o>funnel</o> object. We check to make sure we have two arguments in our message before we proceed in our <m>list()</m> method, as we use both the values in the list in the function. We use the first argument (which slider we moved) to determine which element of the array thevalues we set to the second argument (the value).</p>

<p>Look at the <m><m>bang</m>()</m> and <m>reverse()</m> functions in the JavaScript code.</p>
<p>
<pre><code language="javascript">// <m>bang</m> -- steps through sequencer
function <m><m>bang</m>()</m>
{
   if(seqcounter&gt;=numsliders) // reset sequencer
   {
      seqcounter = 0;
   }
   if(thereverse) // read from the array backwards
   {
      outlet(1, numsliders-seqcounter-1); // send out our location in the sequence
      outlet(0, thevalues[numsliders-seqcounter-1]); // send out the current note
   }
   else // read from the array forwards
   {
      outlet(1, seqcounter); // sound out our location in the sequence
      outlet(0, thevalues[seqcounter]); // send out the current note
   }
   seqcounter++; // increment the sequence
}

// reverse -- changes sequence direction
function reverse(val)
{
   if(arguments.length)
   {
      thereverse = arguments[0]; // flip it
   }
}</code></pre>
</p>

<p>Our <m>bang()</m> method (which in our patch is triggered by a <o>metro</o> object) steps through a sequence of values in a manner analogous to the <o>counter</o> object. The maximum count is set by the number of sliders we have in our patch (defined by <m>numsliders</m>). The direction of the counting is always upwards, snapping back to <m>0</m> when we exceed the number of sliders. The <m>reverse()</m> function sets a variable (<m>thereverse</m>) based on the arguments for a <m>reverse</m> message sent in from Max. This changes the way in which the <m>bang()</m> method reads from the array (<m>thevalues</m>) storing the numbers from our control surface of <o>slider</o> objects. Our two <m>outlet()</m> functions send our current index value out our <o>js</o> object’s right (<m>1</m>) outlet, followed by the value at that index in the sequence out our <o>js</o> object’s left (<m>0</m>) outlet. Note that we follow the important Max convention of outputting values from outlets in a <i>right-to-left</i> order. Otherwise, our <o>pack</o> object would be triggered by its left inlet before it receives the value it needs in its right inlet.</p>

<p>The <m>outlet()</m> function outputs the value at the current index in the sequence out our <o>js</o> object’s left (<m>0</m>) outlet.</p>

<p>Now that you know how the JavaScript code is working, play with the patch some more. Think about how you would recreate the sequencer using the normal Max table and counter objects.</p>

<h2>Summary</h2>

<p>The <o>js</o> object offers you a powerful way to create Max patches dynamically in JavaScript. Object creation is accomplished through the assignment of variables to <i>Maxobj</i> objects created by a <i>Patcher</i> object, which represents the patch in JavaScript. The <m>newdefault()</m> and <m>newobject()</m> methods allow you to create objects, which can be destroyed by a <m>remove()</m> method. The <m>connect()</m> method lets you make patcher connections between Maxobjs in your script. A Maxobj can be assigned to the <o>js</o> object itself through the ‘box’ property to the patcher. When designing JavaScript functions to act as methods for Max messages, the arguments passed with the messages are available through the arguments array from within the function.</p>

<h2>Code Listing</h2>

<p>
<pre><code language="javascript">// autosurface.<o>js</o>
//
// automatically generate a MIDI control surface with
// visual feedback (sliders), hook it up to a <o>funnel</o>
// object, and use it to drive a simple sequencer.
//
// rld, 5.04
//
// inlets and outlets
inlets = 1;
outlets = 2;
// global variables and arrays
var numsliders = 0;
var seqcounter = 0;
var thereverse = 0;
var thevalues = new Array(128);
// Maxobj variables for scripting
var controlin = new Array(128);
var thesliders = new Array(128);
var thefunnel;

// methods start here
// sliders -- generates and binds the sliders in the max patch
function sliders(val)
{
   if(arguments.length) // bail if no arguments
   {
      // parse arguments
      a = arguments[0];
      // safety check for number of sliders
      if(a&lt;0) a = 0; // too few sliders, set to 0
      if(a&gt;128) a = 128; // too many sliders, set to 128
      // out with the old...
      if(numsliders) this.patcher.remove(thefunnel); // if we've done this before, get rid of the <o>funnel</o>
      for(i=0;i&lt;numsliders;i++) // get rid of the <o>ctlin</o> and <o>slider</o> objects using the old number of sliders
      {
         this.patcher.remove(controlin[i]);
         this.patcher.remove(thesliders[i]);
      }
      // ...in with the new
      numsliders = a; // update our global number of sliders to the new value
      if(numsliders) thefunnel = this.patcher.newdefault(300, 300, "<o>funnel</o>", a); // make the <o>funnel</o>
      for(k=0;k&lt;a;k++) // create the new <o>ctlin</o> and <o>slider</o> objects, connect them to one another and to the <o>funnel</o>
      {
         controlin[k] = this.patcher.newdefault(300+(k*100), 50, "<o>ctlin</o>", k+1);
         thesliders[k] = this.patcher.newdefault(300+(k*100), 100, "<o>slider</o>");
         this.patcher.connect(controlin[k], 0, thesliders[k], 0);
         this.patcher.connect(thesliders[k], 0, thefunnel, k);
      }

      // connect new objects to this <o>js</o> object's inlet
      ourself = this.box; // assign a Maxobj to our <o>js</o> object
      if (numsliders) this.patcher.connect(thefunnel, 0, ourself, 0); // connect the <o>funnel</o> to us
   }
   else // complain about arguments
   {
      post("sliders message needs arguments");
      post();
   }
}
// list -- read from the created <o>funnel</o> object
function list(val)
{
   if(arguments.length==2)
   {
      thevalues[arguments[0]] = arguments[1];
   }
// <m>bang</m> -- steps through sequencer
function <m><m>bang</m>()</m>
{
   if(seqcounter&gt;=numsliders) // reset sequencer
   {
      seqcounter = 0;
   }
   if(thereverse) // read from the array backwards
   {
      outlet(1, numsliders-seqcounter-1); // send out our location in the sequence
      outlet(0, thevalues[numsliders-seqcounter-1]); // send out the current note
   }
   else // read from the array forwards
   {
      outlet(1, seqcounter); // sound out our location in the sequence
      outlet(0, thevalues[seqcounter]); // send out the current note
   }
   seqcounter++; // increment the sequence
}

// reverse -- changes sequence direction
function reverse(val)
{
   if(arguments.length)
   {
      thereverse = arguments[0]; // flip it
   }
}</code></pre>
</p>

<seealsolist>
<seealso name="js">Max JavaScript object</seealso>
<seealso name="jsintro" type="vignette" module="js" >javascriptinmax</seealso>
</seealsolist>

</chapter>
