Mixing Processing, Processing.js, JavaScript

Jon and I are just back from London where, among other things, we presented on Processing.js.  In a few days we're off to Chicago to the Mobile Processing conference, too.  At both we're talking about the way Processing.js allows developers/artists to work in Java-style, JavaScript-style, or a hybrid-style of coding.  Today via Twitter I came across this wonderful post by Matt Pearson of how to move from pure Processing to pure JavaScript.  I wanted to talk about the middle way.

Processing.js is intentionally easy on syntax.  We don't ship a pure Processing-to-JavaScript compiler (I'd say "transpiler" but Pomax might throw something at me..) because we don't want to lock our users into Processing (or JavaScript).  We want to allow as much freedom as possible when it comes to how you write your code.

On a technical level we take the code you give us inline (or we download for you via XHR), and run it through the Processing.compile() function.  It builds an AST and allows us to translate things like Java's class inheritance, templating, and other syntax and rewrite to pure JavaScript.  BUT!  We allow you to mix JavaScript in with the Processing code, and simply leave it alone (there are a few edge cases with JS literals, but in practice it's never really an issue).  As Matt writes:

Processing 2.0 adds a third option, “JavaScript”, which allows you to publish direct to HTML5 Canvas (using the now in-built Processing.js). And it works. It just works.
I can't tell you how much work went into making that true, and it makes me smile to read people writing it.  Let's look at an example.  Here's some Processing code stolen lovingly from the Processing project's examples page:

void setup() {  
  size(640, 360);  
  stroke(255);  
  noFill();  
}  
  
void draw() {  
  background(0);  
  for (int i = 0; i < 200; i += 20) {  
    bezier(mouseX-(i/2.0), 40+i, 410, 20, 440,  
           300, 240-(i/16.0), 300+(i/8.0));  
  }  
}

You can try the code out yourself here.  When you run that code through our compiler, you get this:

// this code was autogenerated from PJS  
(function($p) {  
  
    function setup() {  
        $p.size(640, 360);  
        $p.stroke(255);  
        $p.noFill();  
    }  
    $p.setup = setup;  
  
    function draw() {  
        $p.background(0);  
        for (var i = 0; i < 200; i += 20) {  
            $p.bezier($p.mouseX - (i / 2.0), 40 + i, 410, 20, 440,  
                      300, 240 - (i / 16.0), 300 + (i / 8.0));  
        }  
    }  
    $p.draw = draw;  
})

You'll note the $p variable, which is an instance of Processing, and has all the necessary bits of the Processing language and drawing API exposed as methods, getters, etc.  We've also wiped out type information (the i variable isn't an int anymore) and defined our own setup and draw functions, which we've attached to the instance.

That JavaScript was written by a tool, and you or I would likely do it differently.  The point is, you can write any JavaScript you want, using any style you want, using any library you want (and even any syntax you want!).  Matt talks about using a noise algorithm written in JavaScript.  That's fine, you can do that with any JavaScript library in your sketch code, since the sketch code has access to the page's global.  We've written about different ways to access things in the page here and here, so I won't do it again now.  Here's what it might look like:

var perlin;  
float y;  
  
void setup() {  
  size(500,500);  
  // Just call/use your JS libs directly in the Processing code  
  perlin = new SimplexNoise();  
}  
  
void draw() {  
  // Call the noise method, stuff it into our variable y  
  y = perlin.noise(0.01, 0);  
  ...  
}

The point is that you can use as much of Processing or as much of JavaScript as you want, and change the ratio for different projects.  You can also do raw canvas operations from within your sketch:

// Canvas to which this sketch is bound.  
var canvas;  
// Canvas drawing context for the running sketch.  
var ctx;  
  
void setup() {  
  size(500,500); // creates a 2D sketch  
  canvas = externals.canvas;  
  ctx = externals.context;  
}  
  
void draw() {  
  ctx.fillStyle = "rgba(255, 255, 255, 0.1)";  
  ctx.fillRect (0, 0, width, height);  
  ...  
}

This means that you can do things like drawImage() a video element or another canvas (mixing another graphical JS lib with Processing.js, for example). It also means that you can do what's easier in Processing using Processing, and what's easier in raw canvas using raw canvas calls (hint, almost nothing is easier using raw canvas calls).

You can also turn this on its head and only use JavaScript mostly, only calling into Processing when you want some function it provides:

var p = Processing.getInstanceById('sketch-canvas-id');  
var n = p.noise(0.01);  
...

Processing.js is as true to Processing as we can make it. It's also 100% open web. Luckily, we don't have to sacrifice on either in order to make it work. Our users are Processing developers, JavaScript developers, and everything in between.

Show Comments