Generative Typography

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

Discussion Questions

Further Resources

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);
  }
}