CS 2, Winter 2008
Programming for Interactive Digital Arts

Jan 11: Interaction


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.

From static to dynamic sketches

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

To allow us make a dynamic sketch, Processing lets us specify 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. Here's a simple example that plots randomly-colored ellipses at random positions:

void setup()  // Done once at the start of the program
{
  size(400,400);
  noStroke();
}

void draw()  // Done every frame
{
  // Random fill color
  fill(random(255),random(255),random(255));
  // Random-sized ellipse at random position
  ellipse(random(width),random(height),random(25),random(25));
}
screenshot[applet]

In addition to setup/draw, there are a couple of new things in this sketch (refer to the Programming notes section below for more discussion). The first is the random() function, which generates an unpredictable number between 0 and its parameter. (There's also a two-parameter version that lets us set both the low side and the high side.) In the example, we're generating random red, green, and blue components of the fill color, along with random coordinates (up to the width and height of the sketch) and size (up to 25) of the ellipse. The other new thing is the variables width and height, which give the width and height of the window.

As discussed above, 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. (The frameCount variable, used in the example in the first class, holds the number of frames that have been displayed.)

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

void setup()
{
  size(400,400);
  noStroke();
  frameRate(15);
}

void draw()
{
  background(0);  // Erase previous frame (by filling background)
  fill(random(255),random(255),random(255));
  ellipse(random(width),random(height),random(25),random(25));
}
screenshot[applet]

By drawing a very transparent rectangle the size of the window, we can make things fade away instead of being cleared.

void setup()
{
  size(400,400);
  noStroke();
  frameRate(15);
  background(0);
}

void draw()
{
  // Fade away by drawing window-sized transparent rectangle
  fill(0,20);
  rect(0,0,width,height);
  fill(random(255),random(255),random(255));
  ellipse(random(width),random(height),random(25),random(25));
}
screenshot[applet]

Mouse position

Processing has a number of variables related to the mouse. The first couple, mouseX and mouseY, allow us to get the current position. Thus we can easily draw something wherever the mouse is. Here's a variation on the simplest drawing program from the first class, using the fade-out trick from above.

void setup()
{
  size(400,400);
  smooth();
  noStroke();
  frameRate(30);
  background(0);
}

void draw()
{
  fill(0,10);
  rect(0,0,width,height);
  fill(255);
  ellipse(mouseX,mouseY,30,30);
}
screenshot[applet]

Notice that we treat the variables just like numbers, since that's what they hold. (This is the same as the discussion of random, above.) Thus we can add (+), subtract (-), multiply (*), and divide (/), among other things. Here's an example where we "mirror" the mouse position, by subtracting it from the width and/or height of the window:

void setup()
{
  size(400,400);
  smooth();
  noStroke();
  frameRate(30);
  background(0);
}

void draw()
{
  fill(0,10);
  rect(0,0,width,height);
  fill(255);
  ellipse(mouseX,mouseY,30,30);
  // Mirror it:
  ellipse(width-mouseX,mouseY,30,30);
  ellipse(mouseX,height-mouseY,30,30);
  ellipse(width-mouseX,height-mouseY,30,30);
}
screenshot[applet]

We can use the mouse position to indirectly control the sketch. Here's a simple example that sets the red component of the background color based on the x coordinate, and the blue component based on the y component. The formula calculates the position as a fraction of the width (or height), from 0 to 1, and scales that to be from 0 to 255. Also take a look at the Processing example Basics | Input | Mouse2D, which sets sizes according to mouse position.

void draw()
{
  background(255*mouseX/width,0,255*mouseY/height);
}
screenshot[applet]

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

void setup()
{
  size(400,400);
  smooth();
  noStroke();
  frameRate(30);
  background(0);
}

void draw()
{
  fill(0,10);
  rect(0,0,width,height);
  stroke(255);
  line(mouseX,mouseY,pmouseX,pmouseY);
}
screenshot[applet]

Or we can use the two mouse positions to control the drawing in a less direct fashion. The following sketch, inspired by the Processing example Topics | Drawing | Pattern sets the color and the size of the ellipse according to how far the mouse has moved. The red component is the lesser (min()) of 255 and the absolute difference (abs()) between the x coordinates, times 10. The min is because the value might possibly be more than 255 (the maximum allowed). The absolute is because we only want how far, not left vs. right. The times 10 is because typically the mouse doesn't move that far between frames, and we want the color to be bright enough. Similar calculations gives the red component, and the size of the ellipse.

void setup()
{
  size(400,400);
  smooth();
  noStroke();
  frameRate(30);
  background(0);
}

void draw()
{
  fill(0,2);
  rect(0,0,width,height);
  // Set color from how far mouse has moved
  fill(min(255,10*abs(pmouseX-mouseX)), 0, min(255,10*abs(pmouseY-mouseY)));
  // Set size from how far mouse has moved
  ellipse(mouseX,mouseY,abs(pmouseX-mouseX),abs(pmouseY-mouseY));
}
screenshot[applet]

Programming notes

Continuous sketch
Sketches can be continuously updated, drawing a frame for each 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. Specify these according to the following syntax:
void setup()
{
  initialization commands
}

and

void draw()
{
  commands to do each frame
}
Variable
A variable is just a way to keep track of a value by giving it a meaningful name. Processing has some built-in variables, and we'll see later how to define our own. In addition to using variables to hold things that really do vary, it's often good to use them to give name to "magical" numbers that might not convey any meaning when scattered around the code. For example, by using width rather than 400, we know that we're wanting something as big as the screen. Also, if we later decide to change the window size (by calling the size() function in setup()), we don't have to remember to change that magical 400, too.
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.
Math
We can do calculator-like things with numbers, including add (+), subtract (-), multiply (*), and divide (/) them. Other mathematical operations include abs() (absolute value) and min() (minimum). The results of these operations are themselves numbers. There are rules about the order in which the parts of complex expressions are evaluated (e.g., multiplication before addition). Parentheses can help make it clear.
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).

Practice problems

  1. Create a sketch that draws randomly colored lines of random widths at random positions. [hints]
  2. Modify your sketch (or the first sketch from the notes) to fade away based on the x position of the mouse. [hints]
  3. Modify the sketch that draw the ball wherever the mouse is, so that the ball is some random distance away from the mouse, or half the mouse position. Also try setting the size of the ball based on the mouse position. [hints]