It looks 3D right? The reality is that the animation is composed only of curves in a 2D plane, we are going to see in depth the steps that I have followed to create this effect that we can use on our websites and combine with user actions.
Setup
To start we will need to add the p5js library to our project and create a JS file where we will put all the necessary code. We will create a canvas with a black background through the setup function in this way:
function setup() {
createCanvas(window.innerHeight, window.innerHeight);
background(0);
frameRate(100);
stroke(255);
strokeWeight(10);
noFill();
}
The correct way to draw a circle
At the end of the setup function we will create a circle of points through a for loop that iterates the angles of the circle, like this:
function setup() {
...
const radius = 200;
const centerX = width / 2;
const centerY = height / 2;
for (let angle = 1; angle <= 360; angle += 10) {
const x = centerX + radius * cos(radians(angle));
const y = centerY + radius * sin(radians(angle));
point(x, y);
}
}
We will get something like this:
Adding lines
Now we will replace the points with lines that go from each point to the center. And we change the strokeWeight from 10 to 1:
function setup() {
...
const y = centerY + radius * sin(radians(angle));
beginShape();
curveVertex(centerX, centerY);
curveVertex(centerX, centerY);
curveVertex(x, y);
curveVertex(x, y);
endShape();
}
}
Moving the center and crushing the circle
Now we would be seeing our future "jellyfish" from above. To create a side view effect I have decided to move the center of the circle higher on the Y axis and flatten the circle creating this effect:
function setup() {
...
const y = centerY + radius * sin(radians(angle));
beginShape();
curveVertex(centerX, centerY - 100);
curveVertex(centerX, centerY - 100);
curveVertex(x, y / 10 + 500);
curveVertex(x, y / 10 + 500);
endShape();
}
}
Curving the lines
Now our experiment begins to take on more of a jellyfish shape by adding curvature to the lines we just created. To do this we will modify above all the first and last curveVertex that correspond to the anchor points of the curve.
function setup() {
...
const y = centerY + radius * sin(radians(angle));
beginShape();
curveVertex(centerX, centerY + 80);
curveVertex(centerX, centerY - 50);
curveVertex(x, y / 10 + 500);
curveVertex(x, y / 10 + 1200);
endShape();
}
}
Adding lots of lines and noise 🤘🏿
Adding many lines, lowering the opacity of each line and adding perlin noise on the Y axis we get a more organic and natural effect that already reminds us of a jellyfish.
function setup() {
createCanvas(window.innerHeight, window.innerHeight);
background(0);
frameRate(100);
stroke(255, 20);
strokeWeight(1);
noFill();
const radius = 200;
const centerX = width / 2;
const centerY = height / 2;
for (let angle = 1; angle <= 360; angle += 0.2) {
const x = centerX + radius * cos(radians(angle));
const y = centerY + radius * sin(radians(angle));
const noiseY = 50 - noise(angle / 200) * 100;
beginShape();
curveVertex(centerX, centerY + 80);
curveVertex(centerX, centerY - 50);
curveVertex(x, y / 10 + 500 + noiseY);
curveVertex(x, y / 10 + 1200);
endShape();
}
}
More noise, color and movement 🤯
Playing with the values of our curves and with the stroke color of each line adding variation over time we created the complete effect we were looking for. I leave you the complete code of the animation and I encourage you to create your own jellyfish with new shapes and colors and adding user or sound interaction. Happy Coding! 🖖🏻🙌🏾
function setup() {
createCanvas(window.innerHeight, window.innerHeight);
background(0);
frameRate(100);
stroke(255, 20);
strokeWeight(1);
noFill();
}
let i = 0;
function draw() {
i++;
background(0);
const centerX = width / 2;
const centerY = height / 2;
const radius = 200 * noise(i / 300) + 100;
const radius2 = 20 * noise(i / 300) + 20;
strokeWeight(1);
translate(0, -150);
for (let angle = 1; angle <= 360; angle += 0.2) {
const x = centerX + radius * cos(radians(angle));
const y = centerY + radius * sin(radians(angle)) + (200 - noise(radians(angle), i / 100) * 400);
const noiseStrokeR = noise(radians(angle));
const noiseStrokeG = noise(i / 100);
const noiseStrokeB = noise(radians(angle), i / 100);
stroke(
Math.round(255 * noiseStrokeR + 10),
Math.round(120 * noiseStrokeB + 40),
Math.round(255 * noiseStrokeG),
60
);
beginShape();
const noiseY = noise(radius / 100) * 100;
const noiseY2 = 50 - noise(radius / 100, i / 120) * 100;
const noiseX = 500 - noise(radians(angle), i / 120) * 1000;
curveVertex(centerX, centerY + 200);
curveVertex(centerX, centerY - 120 + noiseY);
curveVertex(x, y / 10 + 500 + noiseY2);
curveVertex(x + noiseX, y / 10 + 1000);
endShape();
}
for (let angle = 1; angle <= 360; angle += 20) {
const x = centerX + radius2 * 3 * cos(radians(angle));
const x2 = centerX + (radius2 / 2) * cos(radians(angle));
const y = centerY + radius2 * sin(radians(angle));
const noiseStrokeR = noise(angle / 200);
const noiseStrokeG = noise(i / 100);
const noiseStrokeB = noise(angle / 200, i / 100);
stroke(
Math.round(255 * noiseStrokeR + 10),
Math.round(120 * noiseStrokeB + 40),
Math.round(255 * noiseStrokeG),
120
);
strokeWeight(2);
beginShape();
const noiseY = noise(radius / 100) * 100;
const noiseY2 = 50 - noise(i / 200, angle) * 100;
const noiseX = 1000 - noise(radians(angle), i / 200) * 2000;
const noiseX2 = 100 - noise(radians(360 - angle), i / 200) * 200;
curveVertex(x2, centerY + 200);
curveVertex(x2, centerY - 120 + noiseY);
curveVertex(x + noiseX2, y / 1.1 + 500 + noiseY2);
curveVertex(x + noiseX, y / 10 + 1000);
endShape();
}
}
Top comments (3)
wow!
Awesome! How would you have it move around on the screen? So it would look like its swimming
You could use translate(x,y) and rotate()