Week 2 – art work

Turning Math into Motion: Playing with Sine and Cosine in p5.js

I’ve always liked the concept of sine and cosine waves. They’re simple functions on paper, just waves that go up and down forever. But I was wondering what would it make if it’s involved in creating virtual art. So I decided to use them to make something that looks alive on the screen.

The Concept

The artwork is based on the idea of ripples. Every click on the canvas sends out a circle that grows, but instead of being a perfect circle, it wobbles slightly — almost like the ripple is remembering where it’s been. Over time, these ripples overlap and fade, and the whole thing feels less like a diagram and more like a memory unfolding.

I think that’s what I love about it: it’s math doing what math does, but the output feels alive.

Highlights in the Code

I don’t want to break it down line by line, but a few details stood out to me while I was building this:

1. The Wavy Distortion
let offset = 15 * sin(this.t / 10);
ellipse(this.x, this.y, radius + offset);

Here, the sin() function is doing all the work. Instead of the circle growing in a boring, linear way, sin(this.t / 10) gives me a smooth back-and-forth motion between -1 and 1. Multiplying by 15 stretches that into a range of -15 to +15.

So when I add offset to the radius, the circle doesn’t stay steady. It breathes, gently expanding and shrinking as it grows. That tiny detail is what makes the ripple feel alive rather than mechanical.

2. The Colors That Shift on Their Own
stroke(
map(sin(this.t / 70), -1, 1, 100, 255),
map(cos(this.t / 25), -1, 1, 100, 255),
map(sin(this.t / 30), -1, 1, 150, 255),
180
);

At first, I hard-coded colors, but it looked flat. Then I realized I could let sine and cosine drive the colors, too.

Here’s how it works:

sin() and cos() output values between -1 and 1.

map() stretches those into ranges that make sense for color (e.g., 100–255).

Because the math is always oscillating, the stroke color is always changing — slowly shifting through different shades.

It’s not random; it’s predictably unpredictable. That’s why the transitions feel smooth instead of jarring.

3. Here is where I used loops:
function draw() {
background(10, 10, 20, 30);
for (let r of ripples) {
r.update();
r.show();
}
}

Each click I add one ripple to an array of ripples where I do the get bigger – wave distortion effect. And here I loop over them to update and show the effect.

Every ripple has its own this.t, which increases every frame. That’s what drives everything: the size, the offset wobble, the color shifts. Without t, nothing would move. With it, the math unfolds frame by frame, and the artwork comes alive.

Leave a Reply