Time-Based Animation

We can make the sketch animate by using time as an input. Modify y so that it is a function of time (millis()). This causes the sketch to animate.

function draw() {
	background(100);

	for (let i = 0; i < 20; i++) {
		let x = 10 + 50 * i;
		let y = map(sin(i + millis() / 100), -1, 1, 80, 120);
		circle(x, y, 20);
	}
}

CleanShot 2022-04-08 at 09.32.17.gif

Note: Now that the circles are drawn in a different position each time draw() is called, it is important to clear the canvas (background(200)) at the beginning of draw(). Otherwise each circle will be drawn on top of all the circles drawn by previous calls to draw(). (This has been happening in all the sketches so far, but since all the circles were drawn at the same position, this was not detectable.)

Try this: Change the sketch to animate the size of the object, instead of its position. You can base your work off of the previous two sketches.

Random

The previous sketch uses time to drive one of the shape’s properties (its vertical position). Let’s use random() instead.

function draw() {
	background(100);

	for (let i = 0; i < 20; i++) {
		let x = 10 + 50 * i;
    let y = random(80, 120);
		circle(x, y, 20);
	}
  noLoop();
}

Screenshot of Safari (4-8-22, 11-23-20 AM).png

Screenshot of Safari (4-8-22, 11-23-34 AM).png

Screenshot of Safari (4-8-22, 11-24-00 AM).png

Note: There are three screenshots next to the code, to show that the random() returns a different sequence of values, and therefore the sketch draws a different image, each time it is run.

The noLoop() function prevents draw() from being called again. Let’s see what would happen if we removed it.

function draw() {
	background(100);

	for (let i = 0; i < 20; i++) {
		let x = 10 + 50 * i;
    let y = random(80, 120);
		circle(x, y, 20);
	}
}

CleanShot 2022-04-08 at 11.28.41.gif

Aargh! Just as the previous sketch drew the circles at different positions each time it was run, this one draws them at different positions every frame.

What if we want to remember the position from frame to frame, so that we can draw the circles at the same position each time (or, move them in a more organized way that incorporates their previous position)?

In JavaScript, the way to remember things is to use a variable. A variable can hold a single value, but we want to remember several values: the vertical position of each circle. The trick is that the single value that the variables holds can be an array, which itself holds several values. Here’s what it looks like to use an array to set to hold the y values, inside draw().

function draw() {
	background(100);
	
	let ys = new Array();
	
	for (let i = 0; i < 20; i++) {
		ys[i] = random(80, 120);
	}

	for (let i = 0; i < 20; i++) {
		let x = 10 + 50 * i;
		circle(x, ys[i], 20);
	}
}

CleanShot 2022-04-08 at 11.28.41.gif

Note: You could also write let ys = [] instead of let ys = new Array(). [] is an abbreviation for new Array().

Advanced note: Each time an array item is assigned, as in ys[i] = random(80, 120), JavaScript makes the Array large enough to hold the new item. [Technically this is not quite true. But the effect is similar: you don’t need to worry about making an array large enough when you create it.] But you can also write new Array(20), if you know in advance how large the array needs to be.

As you can see above, simply using an array as intermediary doesn’t help with the jitter if the array is still initialized on each call to draw(). Each call to draw() makes a new array, stores it in a new variable named ys, and fills the array with a new set of random numbers.

Let’s make the array a global variable, and initialize it in setup() instead of draw().