Trying out Processing

At a glance:

I have a go at using the Processing programming language, which was developed to help graphic designers but has a broad bunch of potential applications. While Processing proper sits on top of Java, there's a handy implementation in JavaScript.

13 Sep 2020


A new (to me) language for making cool graphics

I've recently been reading a bit about graphic design and several things independently pointed me towards the Processing programming language. It's really nice! In its own description:

Processing is a flexible software sketchbook and a language for learning how to code within the context of the visual arts. Since 2001, Processing has promoted software literacy within the visual arts and visual literacy within technology. There are tens of thousands of students, artists, designers, researchers, and hobbyists who use Processing for learning and prototyping.

Processing is a compiled language that sits on top of Java. It's explicitly been designed to make it easy for someone undergoing their first programming experience to pick up and start writing simple code "draw a line from here to there", "draw a bezier curve just like so", "add an ellipse with X coloured fill, and Y coloured stroke, just where I click the mouse". The aim is to provide graphic designers with a tool to meet their computational needs that go beyond point-and-click tools, but with minimal entry and learning barriers. I'd say its creators have met their objective.

While Processing proper is a strongly typed Java-like language that supports object-oriented programming, there are a couple of JavaScript versions built to make it easier to embed "sketches" (ie Processing programs) into web pages. p5.js is the best developed. Here is a sample sketch I wrote to familiarise myself with Processing, subsequently re-written in p5.js:

Everyone loves pie charts (ok, I know we don't really), but what's cooler than a pie chart? 200 resizing, elliptical, randomly accelerating pie charts spinning in different directions! Click in the sketch (or even outside it) to reset the pie charts based on that mouse click.

Pointless fact - these pie charts each show the industry composition of value-add in the Australian economy in 2018-19, top seven industries plus 'other'.

Walking through an example program

Let's walk through this simple program. As I'm brand new to this language, and I first wrote it in pure Processing then had to convert it to JavaScript, it's very unlikely to be particularly good practice. But forcing myself to write about it here was a way to motivate me to finish it.

The p5.js program is all contained in a single script many-pies.js. The full script is on GitHub. In the Processing version I had the code split across three files. Processing uses the concept of a "sketch" to organise its files together into something like a project, with each file saved with a .pde suffix and all compiled together into a single program (invisible to the developer) when you hit "run" in the development environment that comes with the language. Each .pde file gets its own tab in the Process development environment like you'd expect. But for my purposes with this blog and the complications of mucking around also with HTML, it was easier to have the many-pies program as single JavaScript script.

Data and global variables

Here's the first chunk of that script. Here, I'm just:

  • defining the data
  • scaling it so each value is mapped to zero to 360 degrees
  • defining pies as an array (empty for now)
  • defining three arrays of the red, green and blue components of colours to use for pie wedges.
//-----------------------Data and global variables-----------------
// Australian value add by industry division, top 7 divisions and 'other'
// Sourced from https://www.abs.gov.au/AUSSTATS/abs@.nsf/DetailsPage/8155.02018-19?OpenDocument
let the_data = [ 51983, 46153, 32061, 25092, 16001, 14240, 13992, 61200 ];

// Calculate total sum of the_data so can use it for scaling:
let sum = the_data.reduce(function(a, b){
    return a + b;
}, 0);
 
// Rescale the data from 0 to 360 (for degrees):
for(let i =0; i < the_data.length; i++){
	the_data[i] = the_data[i] / sum * 360
} 
    
let pies = new Array();

// Brewer colours, Set3 palette
let palr = [141, 255, 190, 251, 128, 253, 179, 252];
let palg = [211, 255, 186, 128, 177, 180, 222, 205];
let palb = [199, 179, 218, 114, 211, 98, 105, 229];

This is definitely not a sustainable and scalable way of doing these tasks! For example, this will only work with pies of exactly eight wedges; a better approach would have the functionality to generate this palette on the fly. And the data is hard-coded into the program, obviously only the sort of thing you'd do with short illustrative examples.

Setup and draw

The second chunk of code is the guts of the program. Every substantial Processing sketch has two main functions: setup() and draw(). Some of the economy of the language comes from how this pre-defined structure mimics the real-world sketching workflow of a graphic designer.

Perhaps some of my earlier chunk above could have been included in setup() but I've chosen to use that function in this case just for setting up the visual elements of the page. In particular, we define the canvas that is going to appear in my web page, associate it with a div I have created in the HTML version of this post; establish that the things we are going to draw have no stroke, only fill; the framerate; and the 200 elements of the pies array. These are created with the PieChart constructor function which we'll come to in the third chunk of code.

The draw() function is pretty simple. It spins, moves, resizes and draws each of the pie charts in turn; then has a straightforward chunk to monitor if the mouse has been pressed and if so to relocate all the pies to wherever that location is. The draw() function will by default in Processing or p5.js loop indefinitely, useful for this sort of animation, games, and many other programs featuring user interaction. Note that the first thing that happens in each iteration of draw() is that it draws a blank black rectangle over the whole canvas with background(0). The process inside draw() is run 60 times a second, each iteration of which is seeing the whole canvas erased and redrawn.

//--------------------------Main sketch program ('setup()' and 'draw()')-------------------
function setup() {
    // create a canvas and move it to 
    // inside the div with id='sketch-holder':
    var canvas = createCanvas(680, 400);
    canvas.parent('sketch-holder');
      
    noStroke();
    frameRate(60);
      
	// Create 200 pie objects, in the centre of the canvas:
	for(let i = 0; i < 200; i++){
	  
		pies.push(new PieChart(the_data, random(1, width / 4), 0, 0, 0, 
                    randomGaussian() * 0.03, random(20, 230)));						   
	}
}

// Main loop
function draw() {
	background(0);
	for(i = 0; i < pies.length; i++){
		pies[i].spin();  
		pies[i].move(0.2);
		pies[i].resize(2);
		pies[i].display(); 
	}
	
    // If mouse pressed, re-create all the pies centred in the mouse location.
    // Half of these will be a new, small size; half will be whatever size they
    // were previously:
    if (mouseIsPressed){
        for(let j = 0; j < pies.length; j++){
            if(random(0,1) > 0.5){
                pies[j].relocate(mouseX - width/2, mouseY - height/2, 10);
            } else {
                pies[j].relocate(mouseX - width/2 ,mouseY - height/2, pies[j].diameter);
            }
        }
    }
}

The PieChart class and its method

Finally, in my third chunk I define the PieChart class and methods to display(), spin(), move(), resize() and relocate() an object of that class. These methods were used earlier in draw(). Each object of class PieChart has values for key parameters such as its diameter, the "startAngle" at which the first wedge of the pie is drawn (changing this gives the illusion of spinning), its current x and y position, its transparency, and the aspect ratio by which it differs from a proper circle. The various methods that act on objects of this class use and manipulate those values. This is the sort of thing that object-oriented programming is good for organising.

//---------------PieChart objects and their methods----------------------------
class PieChart {
 
    constructor(temp_data, temp_diameter, temp_startAngle, 
                temp_x, temp_y, temp_ss, temp_alpha){
        this.data = temp_data;
        this.diameter = temp_diameter;
        this.startAngle = temp_startAngle;
        this.x = temp_x;
        this.y = temp_y;
        this.xv = 0;
        this.yv = 0;
        this.ss = temp_ss;
        this.alpha = temp_alpha;
        this.aspect = random(0.5, 1.5)
    }
     
    display() {

        push();
        translate(this.x, this.y);

        let segmentAngle = this.startAngle

        // Draw the pie, one wedge at a time
        for (let i = 0; i < this.data.length; i++) {

        fill(palr[i], palg[i], palb[i], this.alpha);
        arc(width/2, 
            height/2, 
            this.diameter * this.aspect, 
            this.diameter, 
            segmentAngle, 
            segmentAngle + radians(this.data[i]));
        segmentAngle += radians(this.data[i]); 
        }
        pop();
    }
 
    // spin method
    spin() {
        this.startAngle += this.ss;
    }
 
    // move method  with a speed argument supplied:
    move(sp) {
  
        // change speeds: 
        this.xv += randomGaussian() * sp;
        this.yv += randomGaussian() * sp;

        // if speed too high in one direction, or off screen, accelerate in other direction:
        let v_constraint = 15;
        if(this.xv > sp * v_constraint | this.x > width){
            this.xv -=sp;
        }
        if(this.xv < -sp * v_constraint | this.x < (-width)){
            this.xv += sp;
        }
        if(this.yv > sp * v_constraint | this.y > height){
            this.yv -= sp;
        }
        if(this.yv < -sp * v_constraint | this.y < (-height)){
            this.yv += sp;
        }

        // Change location based on the speed
        this.x += this.xv;
        this.y += this.yv;
    }
 
    // resize method with a scaling for how much to resize by (roughly):
    resize(z) {
        this.diameter += randomGaussian() * z;
        if(this.diameter < (z + 1)){
            this.diameter = 2 * z ; 
        }
    }
 
    // relocate to new position and diameter:
    relocate(newx, newy, newd) {
        this.x = newx;
        this.y = newy;
        this.diameter = newd;
    }
}

Translating from Processing (Java) to p5.js (JavaScript)

I mentioned earlier that I first wrote this program in Processing and then re-wrote it in p5.js. There's an ok Processing transition tutorial on how to do this. I found it a bit of a pain and I think in future I will try to decide early on which dialect I am writing in. If a sketch is going to end up in my blog I will start developing it in p5.js directly.

The three main changes I had to make in translating from Processing to p5.js were:

  • Add this. to the front of variables within methods that refer to the object the method is manipulating.
  • Removing the type specifications (float, int, etc) and replacing them with let. Such a shame - having got a program working with strict typing, it feels positively dirty dropping the strictness.
  • A few specific changes relating to rendering on the screen, for example pushMatrix() becomes push(), we explicitly define the screen as an HTML5 Canvas, etc.

For actually embedding the sketch in this blog post, I found this useful guidance on how to integrate a sketch into a webpage. Most of the other random guidance on doing this on the web - at least when aimed at beginners - seems to be for publishing the sketch as a stand-alone page rather than embedded with other content.

I had to write this post in HTML directly rather than markdown, but that's not that difficult - quite a few pages on my website I have handcoded in HTML, whenever I want to do anything like explicitly define divs, create tabs, etc. In this case, there were two things I needed to do with my Jekyll setup which I note here (so I remember for next time, and in case someone else is interested!):

  • A webpage with a processing sketch needs to load in the program for the sketch and p5.js library in the <header> of the webpage. The p5.js library itself is about 800kb and I only wanted it loaded if the user was looking at a page that needs it. So I added to my _includes/header.html file, which is included in the header of everything on this website, the jekyll snippet {{ page.extraheader }}. And to the YAML at the top of the script for this blog post I added extraheader: <script src="/js/p5.min.js"></script><script src="/sketches/many-pies.js"></script>. Together, these additions mean that for this page, and this page alone (so far), the p5.js library and the many-pies.js program are loaded in.
  • In the body of the post I included <div id='sketch-holder' style='width: 100%;text-align:center;'></div> so the sketch can appear in the midst of my other text of the post. Note that this only worked because of the line canvas.parent('sketch-holder') in the p5.js script, telling it to place the canvas inside the 'sketch-holder' div in the HTML page.

Processing and R!

After I'd written all the above, I discovered there is currently in development a Processing R package which enables users to write Processing sketches in R. Here's an example from its GitHub page:

I haven't looked into this yet. There's also a Python implementation. I'm not sure what value these alternative implementations offer over using either the Java or JavaScript versions. But no harm in people giving it a go, and I will probably find time to have a look at some point.

← Previous post

Next post →