Learn Creative Coding (#3) - Movement and Time: Making Things Animate

What will I learn
- How the draw loop creates animation;
- using frameCount as a clock;
- sin and cos for smooth, organic motion;
- translate and rotate for transforming the coordinate system;
- building a simple bouncing ball.
Requirements
- A modern web browser;
- Episodes #1 and #2 of this series.
Difficulty
- Beginner
Curriculum (of the Learn Creative Coding Series):
- Learn Creative Coding (#1) - What Is Creative Coding? (And Why You Should Care)
- Learn Creative Coding (#2) - Your First Sketch: Shapes, Colors, and the Canvas
- Learn Creative Coding (#3) - Movement and Time: Making Things Animate (this post)
Learn Creative Coding (#3) - Movement and Time: Making Things Animate
This is where creative coding starts to get addictive.
Static images are cool, but animation is where you start feeling like a wizard. You write a few lines of math and suddenly something on screen is alive. Moving, pulsing, breathing.
The draw loop
Remember how I said draw() runs about 60 times per second? That's your animation engine. Each time draw() runs, it's a new frame. If you change something between frames - move a shape, change a color - you get animation.
The simplest example:
let x = 0;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20);
fill(255, 100, 100);
noStroke();
ellipse(x, 200, 40, 40);
x = x + 2;
}
A red circle moving to the right. Every frame we clear the background and draw the circle at a new x position.
Two important things happening here:
background(20)in draw() - this clears the canvas every frame. Without it, the circle would leave a trail (which is sometimes what you want - we did exactly that in episode 1).x = x + 2- we move the circle 2 pixels per frame. At 60fps, that's 120 pixels per second.
frameCount: your built-in clock
p5.js gives you a variable called frameCount that increments every frame. Frame 1, frame 2, frame 3... It never stops, never resets. This is incredibly useful.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20);
fill(255);
noStroke();
let x = frameCount % width; // wraps around
ellipse(x, 200, 30, 30);
}
The % (modulo) operator makes the circle wrap around - when frameCount exceeds 400, it starts back at 0. Instant looping animation, no if-statements needed.
Sin and cos: the magic functions
Alright, here's where it gets good.
If you remember anything from math class about sine and cosine, forget the triangles for now. In creative coding, sin and cos are oscillation generators. They produce smooth, wave-like values that go back and forth between -1 and 1.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20);
fill(255, 100, 200);
noStroke();
// sin gives us a value between -1 and 1
// multiply to control amplitude, add to control center
let y = 200 + sin(frameCount * 0.03) * 100;
ellipse(200, y, 40, 40);
}
A circle bobbing up and down. Smoothly. Endlessly. The 0.03 controls the speed - smaller = slower. The 100 controls how far it moves. The 200 is the center position.
This formula is so important I want to spell it out:
position = center + sin(time * speed) * amplitude
You will use this pattern constantly. For position, size, color, rotation - anything you want to oscillate smoothly.
Now combine X and Y oscillation:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20, 15); // semi-transparent background = trail effect
fill(100, 200, 255);
noStroke();
let x = 200 + sin(frameCount * 0.02) * 150;
let y = 200 + cos(frameCount * 0.03) * 150;
ellipse(x, y, 20, 20);
}
Because sin and cos are offset by a quarter cycle, using them together for X and Y creates circular or figure-8 patterns. Changing the speed multipliers (0.02 vs 0.03) makes the pattern more complex - the circle traces out a path that takes a long time before it repeats. This is called a Lissajous curve and we'll explore it more later.
Also notice background(20, 15) - that's a dark background at very low opacity. Instead of fully clearing each frame, it fades the previous frame slightly. The circle leaves a ghostly trail that slowly disappears. This is one of my favorite tricks.
Multiple things moving
Once you understand the sin/cos pattern, you can apply it to many objects at once with a loop:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20);
noStroke();
for (let i = 0; i < 15; i++) {
let offset = i * 0.4; // each circle has a different phase
let x = 200 + sin(frameCount * 0.02 + offset) * (50 + i * 8);
let y = 200 + cos(frameCount * 0.025 + offset) * (50 + i * 8);
let size = 10 + i * 2;
fill(100 + i * 10, 150, 255 - i * 10, 150);
ellipse(x, y, size, size);
}
}
Fifteen circles, each on its own orbit. The offset gives each one a different starting position in the wave. The orbit radius grows with i. Suddenly you've got something that looks complex and organic, but it's just a for-loop and some sin/cos.
Translate and rotate
So far we've been positioning shapes using their x/y coordinates directly. But p5.js lets you move and rotate the entire coordinate system. This is called a transformation.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20);
// move the origin to the center
translate(200, 200);
// rotate the whole coordinate system
rotate(frameCount * 0.01);
fill(200, 100, 100);
noStroke();
rectMode(CENTER);
rect(0, 0, 100, 100); // drawn at the origin, but the origin moved!
}
A spinning rectangle. We moved the origin to the center of the canvas with translate(), then rotated everything with rotate(). The rect is drawn at (0, 0), but because the coordinate system rotated, the rect spins.
You can nest transformations using push() and pop():
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20);
noStroke();
for (let i = 0; i < 6; i++) {
push(); // save current state
translate(200, 200);
rotate(frameCount * 0.01 + i * (TWO_PI / 6));
translate(100, 0); // move out from center
fill(100 + i * 25, 150, 200);
rectMode(CENTER);
rect(0, 0, 30, 30);
pop(); // restore state
}
}
Six rectangles orbiting the center. Each one rotates by a different angle (evenly spaced around a circle using TWO_PI / 6). Without push() and pop(), the transformations would stack and everything would go haywire.
The bouncing ball
Let's put a few concepts together. The classic bouncing ball - but we'll make it leave a trail.
let x, y, vx, vy;
function setup() {
createCanvas(500, 500);
x = 250;
y = 100;
vx = 3; // velocity x
vy = 2; // velocity y
}
function draw() {
background(15, 25); // trail effect
// update position
x += vx;
y += vy;
// bounce off walls
if (x < 0 || x > width) vx *= -1;
if (y < 0 || y > height) vy *= -1;
// draw
noStroke();
fill(255, 180, 50, 200);
ellipse(x, y, 20, 20);
}
Position + velocity + wall collision. The trail comes from that semi-transparent background trick. You could add gravity by incrementing vy slightly each frame (vy += 0.1), or add friction by multiplying velocities by something like 0.999.
Allez, wat hebben we geleerd?
draw()is your animation loop - runs 60 times per secondframeCountis a free clock that counts framessin()andcos()create smooth oscillation - the core of organic motion- Formula:
position = center + sin(time * speed) * amplitude translate()androtate()move the coordinate system, not individual shapespush()/pop()save and restore transformations- Semi-transparent
background()in draw = trail effect - Velocity + bounce detection = basic physics
Next episode: randomness. We'll learn the difference between random() and noise(), and why Perlin noise is the secret weapon of generative art.
Merci voor het lezen!
X
Alweer een? LOL
Ga eens slapen trut!
XXx
Alee zeg, gij moet ook ni praten he! Wie zit er hier om 2 uur snachts commentaar te plaatsen op MIJN posts?? :-P
Ge zijt precies ook ni aan het slapen, ouwe!
Xx
Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!
Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).
Consider setting @stemsocial as a beneficiary of this post's rewards if you would like to support the community and contribute to its mission of promoting science and education on Hive.
Congratulations @femdev! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)
Your next target is to reach 700 upvotes.
You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP