Programming for Interactive Digital Arts
CS 2, Fall 2023

3. Variation

I can't reiterate enough that the best way to learn this stuff is by doing. Don't just passively skim over these notes. Try each new construct yourself, and see what happens when you change things. Likewise, load the examples in, modify them, see what you get, and come up with new stuff. (And send cool things my way.) We'll do some of that in class, too -- just yell if you want to try a variation on the theme.

The sketches from last class were static and could have been done more easily (and would have looked better) with some drawing program. Of course, our goal is to create dynamic, interactive works. Today we'll begin building up our set of techniques to do so.

That's a little random

The first way to add a little variability to a sketch is not to set parameters exactly ourselves, but let Processing choose a number within some range. For example, the following sketch sets the width of an ellipse to be some random number between 25 and 75. Try running it several times.

Note, the drawing in this example is performed inside our setup function, which means it will be executed once only.
[sketch1]
function setup(){

ellipse(50, 50, random(25,75), 50);

}

This introduces a slight twist on function calls. Last time, they were used as commands, to draw a shape, set the color, etc. Here, the random() function is used to generate an unpredictable number between the low value (first parameter) and high value (second parameter). The number produced by random() is used in some other command, here ellipse(). We say that the random function returns the value. This value is just a number, so it can be used anywhere a number could be, including as the width of the ellipse.

We can use a random call for each of the ellipse parameters, to choose a random position and size. For example, by sprinkling random() gratuitously around our alien sketch from last time, we can make it not stack so perfectly, have variable-sized eyes, etc. Note that if we only give one parameter to random(), it is the high value, and the low value is assumed to be 0.

// based on the Zoog example by Daniel Shiffman in Learning Processing

function setup(){

createCanvas(400,400);          // set screen size
rectMode(CENTER);       // rect commands use center coordinates
background(255);        // set the background color

noStroke();               // do not draw strokes
fill(128);                // set the body
rect(200,200,40,200);     // body
fill(200);                // set the body
ellipse(200,140,120,120); // head

fill(0);                  // set the eye color
ellipse(random(160,180),random(130,150),
        random(20,30),random(40,60));   // eye (randomly changed)
ellipse(random(220,240),random(130,150),
        random(20,30),random(40,60));   // eye (randomly changed)

stroke(0);                // draw back strokes
strokeWeight(5);          // make thicker lines
point(200,190);           // mouth

stroke(128);              // draw gray strokes
line(182,300,random(160,200),320);    // leg (randomly moved)
line(217,300,random(180,240),320);    // leg (randomly moved)  
line(180,200,160,random(180,220));    // arm (randomly moved)
line(220,200,240,random(180,220));    // arm (randomly moved)

}

screenshot

From static to dynamic sketches

To allow us to make a dynamic sketch, Processing lets us specify a pair of functions named setup() and draw(). Our setup() function performs any initialization stuff that needs to be done once at the start, while our draw() function is called repeatedly, every "tick" of a clock, generating the dynamic content. (Refer to the Programming notes section below for discussion of the syntax.) Here's a simple example that plots randomly-colored ellipses at random positions:

[sketch3]
function setup()  // Done once at the start of the program
{
  createCanvas(400,400);
}

function draw()  // Done every frame
{
  // Random fill color
  fill(random(128,255),random(128,255),random(128,255));
  // Ellipse at random position
  ellipse(random(400),random(400),100,100);
}

The draw() function is called every "tick"; these are the frames (like in a movie). One thing setup can do is specify how fast to move from one frame to the next, by calling frameRate() with a number indicating how many frames per second.

By calling background() in the body of the draw function, the new background erases whatever was there in the previous frame.

[sketch4]
function setup()
{
  createCanvas(400,400);
  frameRate(5);       // slow it down
}

function draw()
{
  background(0);  // Erase previous frame (by filling background)
  fill(random(128,255),random(128,255),random(128,255));
  ellipse(random(400),random(400),100,100);
}

By drawing a very transparent rectangle the size of the window, we can make things fade away instead of being instantly cleared. Each tick, a very transparent rectangle is drawn on top of what was there before; that stuff mostly shows through but is dimmer.

[sketch5]
function setup()
{
  createCanvas(400,400);
  frameRate(15);
}

function draw()
{
  // Fade away by drawing window-sized transparent rectangle
  fill(0,20);
  rect(0,0,400,400);
  
  fill(random(128,255),random(128,255),random(128,255));
  ellipse(random(400),random(400),100,100);
}

Mouse position

Suppose we want to draw an ellipse wherever the mouse is. To do so, we need to represent where the mouse is. This is done just like the center of an ellipse, or the location of a point, i.e. with a pair of x and y coordinates. But the mouse position changes with time. To keep track of it Processing provide a pair of variables. A variable is just a holder of information. In this case we have a pair of variables called mouseX and mouseY. We can treat these variables just like numbers, since that's what they hold. (This is the same as the discussion of random, above.) Thus we can easily draw something wherever the mouse is. The following sketch draws an ellipse at the mouse, and uses the fade-out method from above to leave kind of a contrail.

[sketch6]
function setup()
{
  createCanvas(400,400);
  noStroke();
  frameRate(30);
  background(0);
}

function draw()
{
  // Fade away
  fill(0,10);
  rect(0,0,400,400);
  // Ellipse where mouse is
  fill(255);
  ellipse(mouseX,mouseY,30,30);
}

Two other variables, pmouseX and pmouseY, give the position as of the previous frame. We can use this, for example, to draw lines.

[sketch7]
function setup()
{
  createCanvas(400,400);
  frameRate(30);
  background(0);
}

function draw()
{
  fill(0,10);
  noStroke();
  rect(0,0,400,400);
  
  // Line from where mouse is to where it was
  stroke(255);
  strokeWeight(5);
  line(mouseX,mouseY,pmouseX,pmouseY);
}

Programming notes

Function returning a value
While the drawing functions (ellipse, etc.) stand alone to indicate operations to perform, some functions do calculations and give us a value that we can use. Thus just as we can use the number 17 as a red component, we can also use the function call random(255) as a red component, since it returns a number. Note that we don't put a semicolon after such a function call, since it's inside another function call that is the main command.
Continuous sketch
Sketches can be continuously updated, drawing a frame for each tick of the clock. The setup function gives a set of Processing commands to do once at the start, and the draw function gives commands to do for each frame. The "body" of these functions (inside the curly braces) is just like the static sketches we've done before -- a list of commands. Specify setup and draw according to the following syntax:
function setup()
{
  // Initialization commands, done once at the start of the program

}

function draw()
{
  // Done every frame

}
Variable
A variable is just a way to keep track of a value by giving it a meaningful name. Processing provides a number built-in variables (e.g., mouseX); we'll see more later. We'll also see later how to define our own.
Style
As discussed last time, whitespace is only for the benefit of the reader. I'm following a style I learned some time back (e.g., by putting the curly braces on lines by themselves), but you'll see other styles and may adopt your own (though will lose points on your homework if it's hard to read). One important and universal thing is to indent inside curly braces. In fact, menu option Tools | AutoFormat has Processing re-indent things properly (and may help you recognize a mistake).