Animation and Transformations
Students will leave the session with the ability to use motion graphic techniques to create looping sketches.
Class Agenda
Sketching & Presentations | 8:30 - 9:30am |
BREAK 15min | |
Reading & Code Lecture | 9:45 - 11:30am |
BREAK 60min | |
Workshop | 12:30 - 2pm |
Morning Sketch
Create a sketch that changes as the user types.
Weekly Assignment
Create a loading screen or progress bar indicator that loops over time. Additionally brainstorm for your midterm and come ready with an idea for next session.
Readings
- What is Formstorming? by Ellen Lupton
- The Importance of Generative Art by Tyler Hobbs
Discussion Questions
- What do you think of Formstorming as an act of visual thinking? Do you already use methods of visual thinking in your practice?
- What role does repetition play in the ideation and development processes?
- Can generative art be a process for containing and exploring a single idea?
- How can parametric/generative design tools spur our existing creative processes?
- Is there something loss in turning an idea into code and going from a high-level idea to a repeatable set of steps?
Further Resources
- The Rise of Long-Form Generative Art by Tyler Hobbs
- Changes over time by Allison Parrish
- Transformations and Functions by Allison Parrish
Code Lecture
Transformations
Transformations allow us to change properties of the entire draw function without having to focus on any single part. So far we've been changing the fill()
or stroke()
for example but there are other aspects that we can manipulate.
Translate
translate()
moves the origin of the sketch by the x and y parameters that you give it. The origin starts at (0, 0)
and we can update it using translate()
. The following moves the origin by (100, 100)
pixels:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
translate(100, 100);
circle(0, 0, 40);
}
The following moves the origin to the center of the sketch:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
translate(width / 2, height / 2);
circle(0, 0, 40);
}
This can be handy in situations where you don't want to manually compute translation coordinates for a group of functions or an entire section of your sketch.
Scale
Where translate()
changes the origin of where the sketch is drawn, scale()
determines the size of what gets drawn. To use it you specify the new x and y dimensions as percentages where 0.5 means scale by 1/2.
function setup() {
createCanvas(400, 400);
noFill();
}
function draw() {
background(220);
translate(200, 200);
push();
circle(0, 0, 200);
push();
push();
stroke("red");
scale(0.5, 0.5);
circle(0, 0, 200);
pop();
push();
stroke("blue");
scale(2, 2);
circle(0, 0, 200);
pop();
}
Rotate
rotate()
as the name suggests rotates the canvas around it's origin.
function setup() {
createCanvas(400, 400);
angleMode(DEGREES);
}
function draw() {
background(220);
var step = 10;
for (var angle = 0; angle < 90; angle += step) {
rotate(step);
line(0, 0, 200, 0);
}
}
A common technique for rotate()
is to translate()
the origin to the object's x and y coordinate before rotating it. This allows for the object to be rotated in place.
function setup() {
createCanvas(400, 400);
rectMode(CENTER);
}
function draw() {
background(204);
// translate to the center of the sketch
translate(width / 2, height / 2);
var angle = atan2(mouseY - height / 2, mouseX - width / 2);
// rotate the rectangle according to mouseX and mouseY
rotate(angle);
rect(0, 0, 60, 10);
}
Shear
shearX()
and shearY()
allow you to shear the sketch along the X and Y axis. The amount to shear is specified in angles and the objects are always sheared in relation to the sketches origin.
function setup() {
createCanvas(400, 400);
angleMode(DEGREES);
rectMode(CENTER);
}
function draw() {
background(204);
translate(width / 2, height / 2);
// shear 45 degrees on the X-Axis
shearX(45);
// shear 45 degrees on the Y-Axis
// shearY(45);
rect(0, 0, 100, 100);
}
push() and pop()
All of the transformations we talked about accumulate as you call them. For example, using translate(50, 0)
and translate(20, 0)
is the same as writing translate(70, 0)
. You control where exactly these transformations are executed by using push()
and pop()
.
Animation
As we've covered, the draw()
function runs multiple times a second. In computer animation a single call of the function that draws something to a screen is called a "frame". In p5.js we can update how fast things are drawn by using frameRate()
and track the current frame that is being drawn using frameCount
.
function setup() {
createCanvas(400, 400);
frameRate(15);
}
function draw() {
background(220);
text(frameCount, width / 2, height / 2);
}
Using frameCount you can animate your sketches. The following draws a small circle moving across the screen.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
circle(frameCount, width / 2, 50);
}
Loops
You'll notice that frameCount
continues to keep counting up as the sketch is running. Using the Remainder Operator (%)
we can create sketches that loop. The following loop repeatedly counts from 0 to 100.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
circle(height / 2, width / 2, frameCount % 100);
}
Smooth Loops
Now that our loop grows to a certain point we can work to add more smoothness to our animations by calculating the sine of the frameCount
variable.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
// Multiply our frameCount by the amount we want the circle to bounce
var bounceHeight = sin(frameCount) * 20;
circle(width / 2, height / 2 + bounceHeight, 20);
}
Using the sin()
and map()
together we can create the notion of time within our sketch.
var currentFrameRate = 60;
function setup() {
createCanvas(400, 400);
frameRate(currentFrameRate);
}
function draw() {
background(220);
// Map our position in the frameCount loop to an angle
var time = map(frameCount % currentFrameRate, 0, currentFrameRate, 0, 2 * PI);
// Multiply that angle by the amount we want the circle to bounce
var bounceHeight = sin(time) * 50;
circle(width / 2, height / 2 + bounceHeight, 20);
}
You can also use this to animate other characteristics of our sketch:
var currentFrameRate = 20;
function setup() {
createCanvas(400, 400);
frameRate(currentFrameRate);
rectMode(CENTER);
}
function draw() {
background(220);
// Map our position in the frameCount loop to an angle
var time = map(frameCount % currentFrameRate, 0, currentFrameRate, 0, 2 * PI);
// Multiply that angle by the amount we want to shear
var shearAmount = sin(time) * 0.5;
var size = sin(time) * 200;
translate(width / 2, height / 2);
shearX(shearAmount);
square(0, 0, size);
}
Visualizing Waves
var currentFrameRate = 60;
var numberOfCircles = 5;
function setup() {
createCanvas(400, 400);
frameRate(currentFrameRate);
angleMode(DEGREES);
// noLoop();
}
function draw() {
background(220);
// Map our position in the frameCount loop to an angle
var time = map(frameCount % currentFrameRate, 0, currentFrameRate, 0, 360);
// Step, allows us to push the circle further along the line
var fullLoop = 360;
var step = fullLoop / numberOfCircles;
for (var index = 0; index < numberOfCircles; index += 1) {
// Map the x position on a line
var xOffset = map(index, 0, numberOfCircles - 1, -100, 100);
var x = width / 2 + xOffset;
// Calculate the step for this circle;
var bounceStep = step * index;
var bounceHeight = sin(time + bounceStep) * 50;
var y = height / 2 + bounceHeight;
circle(x, y, 40);
}
}