Interaction

One of the essential components of computer based art is the possibility for interaction.

Interactive design gives the user the ability to change the design. Sometimes the changes are small, sometimes they are large -- the artist has the ability to control how much the user can change.

In JavaScript, we use events to detect when a user interacts with a program. We can detect things like mouse movement, mouse clicks, key board inputs and change in orientation or touches in mobile devices. Today we'll start with some simple interactions.

Drawing a line

We looked at the system variables mouseX and mouseY, which track the position of the mouse, as well as pmouseX and pmouseY that tracks the previous position. We can use these simple values to create a drawing program.

function setup() { createCanvas(400, 400); background(220); } function draw() { line(mouseX, mouseY, pmouseX, pmouseY); }

mousePressed

Notice that the background function appears in the setup function. The reason the line segments persist in the program is that we are not continuously redrawing the background, which would cover the older line segments.

We can overwrite the drawing by introducing the background with the mousePressed event.

function setup() { createCanvas(400, 400); background(220); } function draw() { line(mouseX, mouseY, pmouseX, pmouseY); } function mousePressed() { background(220); /* try adding transparency to the background */ // background(220, 127); }

Like setup and draw, mousePressed is a function that is invoke by the p5 library but defined by us. The code we place inside the brackets after mousePressed() is run whenever either mouse button is pressed.

mouseDragged & keyPressed

mouseDragged also detects a mouse button press, but continues exectuting until the mouse button is released.

keyPressed executes whenever any key is pressed.

Let's look at another example where we can add shapes to the screen and clear them with a key press.

function setup() { createCanvas(400, 400); background(220); noStroke(); } function mouseDragged() { var x = mouseX; var y = mouseY; var s = mouseX / 20 + mouseY / 20; // size ellipse(x, y, s); } function keyPressed() { console.log(keyCode); background(200, 200); }

Position

We can extend the interactive drawing to apply to more shapes or combinations of shapes.

Using the self portrait drawing as a basis, if I want to move the whole thing, I need to add offsets to the shapes so they move relative to the same point.

function setup() { createCanvas(400, 400); rectMode(CENTER); } function draw() { background(200, 25); var x = mouseX; var y = mouseY; var offset = 20; noStroke(); ellipse(x, y, 100); // face stroke(0); ellipse(x - offset, y - offset, 20); // right eye ellipse(x + offset, y - offset, 20); // left eye rect(x, y + offset, 60, 20, 10); // mouth }

Color

So far we have made the shapes position interactive. Using the same principle, we can make the color interactive.

function setup(){ createCanvas(255, 255); } function draw(){ background(0); var r = 0; // red; var g = mouseX; // green var b = mouseY; // blue fill(r, g, b); var x = width / 2; var y = height / 2; var s = width / 2; ellipse(x, y, s); }

Notice in the previous example the canvas width and height are both 255. Remember the RGB color scale? All the values are 0 - 255.

If the canvas is made larger we end up with values that can't be used as color values, anything above 255 will just be read as 255.

We can use the map function to take one range, mouse position, and map it to another range, color value.

Let's look at that example again, using map.

function draw(){ background(0); var r = 0; // red; var g = map(mouseX, 0, width, 0, 255); // green var b = map(mouseY, 0, height, 0, 255); // blue fill(r, g, b); var x = width / 2; var y = height / 2; var s = width / 2; ellipse(x, y, s); }

In the previous examples, the color is always changing, but what if we wanted to save a color?

Using global variables, we can save red, green and blue values.

var r = 255, g = 255, b = 255; // initialize rgb globally function draw() { background(0); fill(r, g, b); var x = width / 2; var y = height / 2; var s = width / 2; ellipse(x, y, s); } function mousePressed() { r = map(mouseX, 0, width, 0, 255); // red g = map(mouseY, 0, height, 255, 0); // green b = map((mouseX + mouseY) / 2, 0, width + height, 0, 255); // blue }

Size

Of course, we can also interact with the size of shapes. Position, size and color are the three attributes of shapes that we can change using interaction.

var s = 10; function draw(){ background(0); fill(255); var x = width/2; var y = height/2; ellipse(x, y, s); } function mousePressed() { s += 10; }
function draw(){ background(0); fill(255); noStroke(); var s = width - mouseX; // reverses the direction var x = width/2; var y = height/2; ellipse(x, y, s); // uncomment to visualize these values // visualize(); function visualize() { fill('plum'); text(width + ' - ' + mouseX + ' = ' + (width - mouseX), mouseX, 20); text('width - mouseX', mouseX, 40); stroke('plum'); line(x - s/2, y, x + s/2, y); text(s, x, y - 10); } }

Meaningful interaction

These techniques show how to make things move when you click or move the mouse, but they're kind of arbirtrary. Good interaction should be meaningful for the user. That can be a lot of different things, it can be informative, challenging, fun or any number of experiences.

The key to meaningful interaction is interacting with the drawing in a way that relates to a theme or action.

For a very simple example, I will make my self portrait talk.

In a previous class, I made a self portrait using variables. Now we'll see those variables come in handy.

Using a simple event, I will make the self portrait talk.

// self portrait function setup() { createCanvas(400, 400); } function draw() { background(220); rectMode(CENTER); var x = width/2; var y = height/2; var s = 200; // size var o = 50; // offset noStroke(); fill("plum"); ellipse(x, y, s); // face fill("yellow"); ellipse(x - o, y, 25); // right eye ellipse(x + o, y, 25); // left eye fill('plum'); stroke("#ffddff"); strokeWeight(4); var mouthSize = map(mouseX, 0, width, 5, 200); // move mouth with mouse x mouthSize = max(0, mouthSize); // prevents negative value error rect(x, y + o, s/2, mouthSize, 10); // mouth }