Processing Live tutorial

The code that was produced in this workshop can be found here.

Processing

Download the Processing IDE (3.x is stable, 4.x is still in beta)

Processing is Java (17). Wrapped in simple libraries.

Drawing a line in plain Java:

public static void main(String[] args) {
    JFrame f = new JFrame();
    JPanel p = new JPanel() {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawLine(0, 0, 100, 100);
        }
    };
    f.add(p);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);
}

Drawing a line in Processing (immediate mode):

line(0,0,100,100);

The basic anatomy of a processing program (sketch):

// Called once at the start:
void setup(){
    // Specify the size of the window
    size(800, 600, P2D); // P2D makes it faster by using the GPU

    // Initialization code here
}

// called 60 times per second until the program stops:
void loop(){
    background(0, 0, 0); // Actually clears the screen with a color

    // Use built in functions to draw things:    
}

Very important:

the Reference

Drawing things

Basic shapes:

ellipse(100, 100, 100, 300);
circle(400, 300, 40);
rect(500, 400, 200, 150);
line(30, 50, 400, 500);

Appearance:

// everything drawn after this function will be color (255,200, 10)
fill(255, 200, 10); 
ellipse(100, 100, 100, 300);
circle(400, 300, 40);

// outline everything drawn after this function will be color (40,200, 100)
stroke(40, 200, 100); 
rect(500, 400, 200, 150);

// Everything is drawn with thick outlines
strokeWeight(3);
line(30, 50, 400, 500);

// Reset appearance for the next frame
fill(255);
stroke(0);
strokeWeight(1);

Variables

Java is typed. You need type specifications to allocate pieces of memory (variables):

int xPosition = 10; // integer, no fractions
float radius = 13.5; // fractions
String yourName = "Junior the 3rd"; // text

Local variables

Variables created in functions will be destroyed when the function is over:

// variables xPosition and yPosition will be created every time draw() is called, 60 times per second
void draw(){
    float xPosition = 20;
    float yPosition = 45;
    circle(xPosition, yPosition, 200);
}

// this won't work. The variables don't exist outside of the draw() function
void updatePosition(){
    xPosition+=10;
    yPosition-=40;
}

Global variables

Frowned upon by many but quite handy for quick hacks. Variables exist and can be shared between functions.

// variables xPosition and yPosition can be called in each function
float xPosition = 30;
float yPosition = 60;

void draw(){
    circle(xPosition, yPosition, 200);    
}

// this will now work.
void updatePosition(){
    xPosition+=10;
    yPosition-=40;
}

Processing defined variables

Variable Purpose
width The width of the window
height The height of the window
mouseX The x-coordinate of the mouse
mouseY The y-coordinate of the mouse
pmouseX The previous x-coordinate of the mouse
pmouseY The previous y-coordinate of the mouse
mousePressed Wether a mouse button is pressed
mouseButton The last mouse button pressed
key the last key pressed
keyCode the last key pressed, also for non-character keys

Movement

Animation is just changing images really fast.

Draw a circle at a different location for every frame:

float xPosition = 0;
float xVelocity = 1;

void draw(){
    circle(xPosition, height/2, 20);
    xPosition += xVelocity;
}

In y and y plane and bounce:

float xPosition;
float yPosition;
float xVelocity = 2;
float yVelocity = 3;

void setup(){
  size(800, 600, P2D);
  xPosition = width/2; 
  yPosition = height/2;
}

void draw(){
    circle(xPosition, yPosition, 20);
    xPosition += xVelocity;
    yPosition += yVelocity;

    // bounce
    if (xPosition > width || xPosition < 0) xVelocity = -xVelocity;
    if (yPosition > height || yPosition < 0) yVelocity = -yVelocity;
}

Multiple objects

Everything that is drawn in Processing is committed to the screen as pixels. You can’t change the location of the pixels afterwards, like in InkScape. It’s rather like Photoshop. To have multiple objects, means you have to draw each object from scratch for each frame. Multiple objects means multiple variables:

float xPositionBall1;
float yPositionBall1;
float xVelocityBall1 = 2;
float yVelocityBall1 = 3;

float xPositionBall2;
float yPositionBall2;
float xVelocityBall2 = 2;
float yVelocityBall2 = 3;

//etc...

This gets really messy. We can use classes to “bundle” variables together. A class is like a recipe for an object, in this case a ball.

// The class is like a recipe
class Ball {
    float xPosition;
    float yPosition;
    float xVelocity=2;
    float yVelocity=3;
}

// The class is used like any datatype (such as int, float, String, etc.)
Ball ball =  new Ball();
Ball ball2 = new Ball();

void draw(){
    circle(ball.xPosition, ball.yPosition, 10);
    circle(ball2.xPosition, ball2.yPosition, 10);

    // Update the variables in the objects
    ball.xPosition+=ball.xVelocity;
    ball.yPosition+=ball.yVelocity;
    ball2.xPosition+=ball2.xVelocity;
    ball2.yPosition+=ball2.yVelocity;
}

Still messy. Lot’s of copied code. Lets make the ball update and draw itself:

class Ball {
  float xPosition;
  float yPosition;
  float xVelocity;
  float yVelocity;

  // A function with the name of the class is called when a
  // variable is made with this class
  Ball() {
    // This puts this ball in a random position with a random velocity
    xPosition = random(0, 800);
    yPosition = random(0, 600);
    xVelocity = random(-5, 5);
    yVelocity = random(-5, 5);
  }

  // The ball knows how to draw itself
  void draw() {
    circle(xPosition, yPosition, 100);
  }

  // The ball knows how to updates it's position
  void update() {
    xPosition += xVelocity;
    yPosition += yVelocity;
    // bounce
    if (xPosition > width || xPosition < 0) xVelocity = -xVelocity;
    if (yPosition > height || yPosition < 0) yVelocity = -yVelocity;
  }
}

// Create two variables using the class
Ball ball1 =  new Ball();
Ball ball2 = new Ball();

void setup() {
  size(800, 600, P2D);
}

// Only call functions from both balls
void draw() {
  ball1.update();
  ball2.update();
  ball1.draw();
  ball2.draw();
}

Lots of balls

Using an ArrayList it’s possible to store and draw lots of balls.

// with the previously described "ball" class included:

// Make a lost to put balls in:
ArrayList<Ball> balls = new ArrayList<Ball>();

void setup(){
    size(800, 600, P2D);

    // Add 100 balls to the list
    for (int iBall=0; iBall<100; iBall++)
        balls.add(new Ball());
}

void draw(){
    // Traverse the list and update each ball
    for (int iBall=0; iBall<100; iBall++){
        balls.get(iBall).update();
    }

    // Use a for-each loop to draw each ball
    for (Ball ball : balls){
        ball.draw();
    }
}

My favorite tricks

Friction

// Make the velocity for an object slightly smaller for each frame:
xVelocity *= 0.99;
yVelocity *= 0.99;

Gravity

// Add a small value to the velocity for each frame, in the ball update
yVelocity += 1;

Follow the mouse

// Make the velocity of the object point towards the mouse
// And scale it down to slowly move towards the mouse
xVelocity = (mouseX - xPosition)*0.05;
yVelocity = (mouseY - yPosition)*0.05;

Random walk

// Add a random number to the velocity for each frame
xVelocity += random(-2, 2);
yVelocity += random(-2,2);

Springs(!!)

Like “follow the mouse” but with acceleration

// By adding a force/acceleration
float springiness = 2.0;
float energyLoss = 2;
xAcceleration = springiness * (mouseX - xPosition) - energyLoss * xVelocity;
xVelocity += xAcceleration/60.0;
xPosition += xVelocity/60;

yAcceleration = springiness * (mouseY - yPosition) - energyLoss * yVelocity;
yVelocity += yAcceleration/60.0;
yPosition += yVelocity/60;

Cheap motion blur

// Add to setup
noStroke(); // turns off stroke

// at start of draw, instead of background:
fill(0,0,0,5); // set the fill-color to black, almost fully transparent
rect(0,0,width, height); // draw a black, almost transparent rectangle on the entire window

// Don't forget to set the fill color back to the ball colors.
fill(255);

Smoke

// In the setup, set the blend-mode to additive
blendMode(ADD);

//In draw() make the balls grey transparent.
fill(200, 200, 200, 10);

// In the ball.update, make the balls go up and re-appear at
// the bottom when it hist the top
if (yPosition < 0) yPosition = height;

Determine start position and start velocity

Using the constructor, the function that is called when a ball is created, you can give new balls a starting position.

class Ball {
    Ball(float x, float y, float vX, float vY){
        xPosition = x;
        yPosition = y;
        xVelocity = vX;
        yVelocity = vY
    }

    // etc...
}

Add new balls

Add a new ball to the list when the mouse is pressed.

void draw(){
    
    // Somewhere in the frame:
    if (mousePressed){
        balls.add(new Ball(mouseX, mouseY, random(-4,4), random(-4, 2)));
    }

    // etc.
}

Serial Communication

In Arduino:

  • open the example 01.Basics->AnalogReadSerial
  • Upload to any device
  • Check the serial output

In Processing:

  • open the example Serial->SimpleRead
  • fill in your serial port at line 22, for instance:
String portName = "/dev/cu.usbmodem142401";
  • Add the following line at line 30:
println(val);

What happens? What values do you see? Why?

  • Replace Serial.println(sensorValue); with Serial.write(sensorValue);
  • Check the output:
    • In the serial terminal
    • In processing
  • Still not right? Why?

ASCII vs Binary

In Arduino, Serial.println() converts values to ASCII:

Serial.println(1234);

actually outputs a ‘1’, a ‘2’, a ‘3’ and a ‘4’. Four bytes. Not one number.

Serial.write(1234);

actually outputs one byte which can only go from 0 to 255. So 1234 is too large. To properly write a number as bytes:

uint16_t bigNumber = 1234;
// Three ways to send a 16 bit integer:

// With arithmetic
Serial.write(bigNumber%256); // Send least significant byte
Serial.write(bigNumber/256); // Send most significant byte

// with binary operations and shifting:
Serial.write(bigNumber | 0x00FF); // Send least significant byte
Serial.write((bigNumber | 0xFF00)>>8); // Send least significant byte

// By sending the bytes as an array
Serial.write((uint8_t*)&bigNumber, 2);

In Processing:

Serial.read() only reads one byte. To read multiple bytes and make an integer:

byte leastSignificantByte = Serial.read();
byte mostSignificantByte = Serial.read();
int reassembledInteger = mostSignificantByte*256 + leastSignificantByte;

Arduino datatypes

Using specified datatypes helps you understand how many bytes are used for each variable:

Datatype description number of bytes range
uint8_t unsigned 8-bit integer 1 byte From 0 to 255
uint16_t unsigned 16-bit integer 2 bytes from 0 to 2^16
uint16_t unsigned 32-bit integer 4 bytes from 0 to 2^32
int8_t signed 8-bit integer 1 byte From -128 to 127
int16_t signed 16-bit integer 2 bytes from -2^15 to 2^15
int16_t signed 32-bit integer 4 bytes from -2^31 to 2^31

These don’t exist in processing.

Simple command structure

  • Send a character byte first
  • Send parameters after that
  • Know exactly how many bytes you’re going to receive

Example, Processing:

///Set the color of an RGB LED
Serial.write('C'); // command 'C' for color
Serial.write(255);
Serial.write(10);
Serial.write(100);

Arduino:

uint8_t command = Serial.read();
switch (command) {
    case 'C':
        r = Serial.read();
        g = Serial.read();
        b = Serial.read();
        break;
    // parse more commands if needed
}