WEEK SIX NOTES

 

Extending Processing.js by mixing Processing and JavaScript Libraries

This week we look at how we can use external libraries, files, and frameworks with our Processing canvas.

(the following is adapted from work done by Mike "Pomax" Kamermans).

Using Processing.js inevitably leads to the desire to mix in other Javascript code with the Processing code in order to take advantage of all the great Javascript libraries that exist are are emerging. A problem with any brute force method is that once any javascript is added directly, Processing.js will no longer accept the sketch. This is inconvenient. However, there are ways to write an interface to keep the two separate and yet compatible within a Web page or application.

The trick is to keep javascript and Processing code separate in a well-managed manner. In order for Processing to not complain, we need to call Processing methods from our Processing code, not Javascript functions as the browser otherwise expects to do. We can direct Processing to properly call our external JavaScript by using the built-in interface Processing provides. We implement the interface according to some rules (an example follows here):

First, we create a function in Javascript that Processing will call from within a Processing interface:

    function myfunction(text, somenumber) {
        // do something with one text parameter and one number parameter
    }
  

Second, we match the JavaScript function signature in a Processing interface within our Processing code:

    interface JavaScript {
        void myfunction(String text, float somenumber);
    }
  

By doing this, we promise Processing that a function with this signature will exist in our Processing code

Third, we set up a JavaScript object and a setJavaScript method to handle the communications between Processing and JavaScript:

    // the object that will point to the on-page javascript
    JavaScript javascript = null;
	
    // a method for binding the page's javascript environment to the above object
    void setJavaScript(JavaScript js) { javascript = js; }
  

Fourth, we use each of our functions at least once in our Processing code within the setup() and/or draw() functions (using our JavaScript object to manage them):

    void draw() {
        [...]
        if(javascript!=null) { javascript.myfunction("moo",45.657); }
        [...]
    }
  

Fifth, we make sure that when we load our sketch, we create a Processing instance, and keep the reference so we can call public methods (including our JavaScript functions):

    var pjs = new Processing(canvasid, "source code");

    pjs.setJavaScript(this);
  

We call the setJavaScript() method, in which Processing understands as "the argument we're passing is actually a JSInterface object". In fact, it's the javascript "window" instance (which Processing need not know). Processing will now happily run your sketch, because even though you're calling a javascript function, you've told Processing that this function also exists, even though it does nothing natively, because of the JavaScript interface. Because there is no type validation when calling sketch methods directly from javascript, we can pass a reference to javascript's "window" object to the sketch as if it counts as a JavaScript object, so javascript won't complain either.

And to show that it actually works, an example. Have a look at the sketch code here, the javascript functions it will call here, and the bit of loader javascript here. In Processing, all you see is a circle going round on a plain background. However, there are "javascript" calls in the code that also write a log entry whenever the circle has gone round a full iteration, and a constant changing of the <canvas> border width as the draw() method is being called. These will do absolutely nothing in Processing, but embedded on a webpage these will trigger the corresponding javascript functions seen at the bottom of the page (from the mycustomfunctions.js file), connecting to the Processing code loaded as a .pde file (as seen in the loader.js file). Many functions can be implemented this way to bind with a powerful library of features written in JavaScript (I used this method for the watershed scenario comparing application that I demonstrated in class.


/**
 * Update the log with some text
 */
function updateLog(text) {
	var e = document.getElementById('log');
	if(e) { e.innerHTML += text + "<br/>\n"; }
}

/**
 * change the border width for the <canvas> element
 */
function setBorderThickness(thickness) {
	var val = (1 + Math.abs(thickness)) + "px";
	document.getElementById('sketch').style.borderWidth = val; 
}

Other examples of extending Processing:

  A 3-D OBJ file loader
  P5

And note that the Processing folks are building their own add-on libraries and facilitating third-party contributions for download.

[one entry per full iteration]