Using Javascript to Control Processing
In a previous entry I mentioned that one of the tantalizing things about Processing.js is the ability to write Processing-like code, but using Javascript instead of Java 1.3 syntax for writing the logic portions. However, I didn't give an example of doing that, so this morning I decided to put together a simple one, and here it is.
Setup
- Create a folder to store your files in.
- Download [Processing.js][processing.js] into that folder.
- In that same folder, create two empty files: test.html and myapp.js.
test.html
Next, we need to fill in test.html, with some mostly generic code.
<html>
<head>
<title>Processing.js Example, Two</title>
<script type="text/javascript" src="processing.js"></script>
<script type="text/javascript" src="myapp.js"></script>
</head>
<body onload="draw_myapp();">
<h1> Processing.js Example, Two </h1>
<canvas id="myapp" width="150" height="150"></canvas>
</body>
</html>
Other than the references to myapp this is a basic template you can use for other projects using Processing.js featuring Javascript as the control language.
myapp.js
All that's left to do is translating some Processing code into Javascript. This will all be contained in the myapp.js file. Because I was playing with it yesterday, I picked the Setup and Draw example, which gives a pretty good overview of the translating process.
First lets look at the Processing version of the program.
void setup()
{
size(200, 200); // Size should be the first statement
stroke(255); // Set line drawing color to white
frameRate(30);
}
float y = 100;
void draw()
{
background(0); // Set the background to black
y = y - 1;
if (y < 0) { y = height; }
line(0, y, width, y);
}
Because it takes advantage of the magic built into Processing, the above code is very concise. Keep that thought in mind, because we'll touch on it in a bit. Before that, lets look at the contents of myapp.js, and see my Javascript implementation.
function draw_myapp(){
function setup_processing() {
var canvas = document.getElementById('myapp');
return Processing(canvas);
}
function setup_myapp(p) {
p.size(300,300);
p.stroke(255);
}
function draw(p) {
p.background(0);
y = y - 1;
if (y < 0) y = p.height;
p.line(0, y, p.width, y);
}
var y = 100;
var frameRate = 90;
var interval = 1000.0 / frameRate;
// setup and run
var p = setup_processing();
setup_myapp(p);
setInterval(function () { draw(p); }, interval);
}
The most obvious difference here is that the Javascript version is 22 lines, compared to the Processing version's 14. Beyond that, the Javascript version is more verbose. We'll take a look at improving upon that in a moment, but first you should open test.html in a browser and you'll see that we have successfully translated the example into Javascript.
Improving the Javascript Version
Like I briefly mentioned above, the reason that the Processing code is particularly concise is because of some of the magic imbedded in Processing. In Processing, we define the draw method, but we don't explicitly use it anywhere (Processing already knows what to do with it). We define the framerate using frameRate, but the loop using that framerate gets created out of sight. Processing doesn't obsess about keeping namespaces clean, its just trying to keep things simple1.
The Javascript version doesn't have any of that magic, and is consequently much more verbose and cluttered. Gazing at that realization, we have to step back and consider whether we want to implement some of that convenient magic or stick with the robust predictability. Personally, I think that the entire allure of Processing is its magic--making it easy for anyone to write impressive things--so I have to answer by saying "Yes please, I want the magic tricks."
Lets see what we can do about that.
First, I wrote a small utility library that contains the magic for this example.
var GLOBAL_PROCESSING;
function set_canvas(id) {
GLOBAL_PROCESSING = Processing(document.getElementById(id));
}
function size(height, width) { GLOBAL_PROCESSING.size(height, width); }
function stroke(color) { GLOBAL_PROCESSING.stroke(color); }
function background(color) { GLOBAL_PROCESSING.background(color); }
function line(x1, y1, x2, y2) { GLOBAL_PROCESSING.line(x1, y1, x2, y2); }
var draw = function() {}; // stub to replace
function frameRate(rate) {
var interval = 1000.00 / rate;
setInterval(draw, interval);
}
function height() { return GLOBAL_PROCESSING.height; }
function width() { return GLOBAL_PROCESSING.width; }
Basically we've recreated a sliver of the Processing magic by thoroughly abusing the global namespace. Assuming you can cope with the moral implications, lets take a look at the new Javascript version of our little program:
setup = function() {
size(300,300);
stroke(255);
frameRate(90);
}
draw = function() {
background(0);
y = y - 1;
if (y < 0) y = height();
line(0, y, width(), y);
}
set_canvas("myapp");
var y = 100;
setup();
Its gone from 22 lines to 14, and is now the same size as the original Processing example. Really, it is almost identical to the original version, which doesn't make a particularly strong case for my argument that sketching code in Javascript is more enjoyable than sketching code in old Java syntax. Still, in a more complex example I think the advantages of Javascript would be more evident2.
(Edit 5/11 21:43 The above code won't quite work as in, it actually needs to be wrapped in a function named draw_myapp, so that it is loaded at the appropriate time. Doing so adds an extra two lines of code, making the Javascript version 16 lines instead of 14. Bah.)
Ending Thoughts
Although I'm all for Javascript as a control language, I'm not sure if the benefits are worth splintering developer knowledge of Processing, nor am I certain if these imagined benefits justify writing the a library that would create a more magical interface layer over Processing.js. It certainly is a loss that the programs written using it couldn't be used with the Processing desktop program, but its not a loss without some compensating benefits.
What are other peoples' thoughts?
Which sounds like a poor attempt at speaking about the Tao.↩
Many of the helpful features in Javascript that Java 1.3 lacks--better looping constructs, first order functions, simple metaprogramming, concise syntax for dictionaries, etc--don't come into play in such a simple example, but open up a number of interesting and exciting doors.↩