p5: Using Sine wave in WEBGL to change y-axis - javascript

I have some spheres that I want to move straight on the x axis with only their y-axis changing based on the sine wave (you know the up and down yo-yo pattern). It is pretty simple in 2d but for some reason, in WEBGL, the angle changes in the sin function is not working for this.y.
let particles = [];
let quantity = 20;
function setup() {
createCanvas(300, 300, WEBGL);
for (let i = 0; i < quantity; i++) {
let particle = new Particle();
particles.push(particle);
}
}
function draw() {
background(15);
orbitControl();
directionalLight([255], createVector(0, 0, -1));
translate(-width / 2, 0);
particles.forEach(particle => {
particle.update();
particle.edges();
particle.show();
});
};
// particle.js
function Particle() {
this.angle = 0;
this.r = 10;
this.x = random(width);
this.y = sin(this.angle) * 200; // I am changing this angle later but it's not working
this.z = random(-50, 50);
this.pos = createVector(this.x, 0, 0);
this.vel = createVector(2, this.y, 0);
this.update = function() {
this.pos.add(this.vel);
this.vel.limit(4);
this.angle += 3;
// print(this.pos);
}
this.edges = function() {
if (this.pos.x >= width) {
this.pos.x = 0;
}
}
this.show = function() {
noStroke();
push();
translate(this.pos.x, this.pos.y, 0);
sphere(this.r);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>

You only call sin one time in the construtor. You probably meant to apply it to this.y on every frame based on this.angle, or some other incrementing value. Also, there are duplicate vectors; pick either an x/y pair or use a vector x/y, but not both.
let particles = [];
let quantity = 20;
function setup() {
createCanvas(300, 300, WEBGL);
for (let i = 0; i < quantity; i++) {
let particle = new Particle();
particles.push(particle);
}
}
function draw() {
background(15);
orbitControl();
directionalLight([255], createVector(0, 0, -1));
translate(-width / 2, 0);
particles.forEach(particle => {
particle.update();
particle.edges();
particle.show();
});
};
// particle.js
function Particle() {
this.angle = 0;
this.r = 10;
this.x = random(width);
this.y = 0;
this.update = function() {
this.angle += 0.05;
this.y = sin(this.angle) * 100;
}
this.edges = function() {
if (this.x >= width) {
this.x = 0;
}
}
this.show = function() {
noStroke();
push();
translate(this.x, this.y, 0);
sphere(this.r);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

Related

How can I integrate an app made with create-react-app with p5.js, with a specific goal to make sound visualization that responds to played sound?

I have read a good number of related posts, but I feel they are not exactly giving me the required solution.
I want to implement in React, the following p5.js JavaScript code that I have written following this tutorial on YouTube => https://www.youtube.com/watch?v=uk96O7N1Yo0:
let sound;
let fft;
let image;
let particles = [];
function preload() {
sound = loadSound("sound-path.mp3");
image = loadImage("image-path.jpg");
}
function setup() {
createCanvas(windowWidth, windowHeigh);
angleMode(DEGREES);
imageMode(CENTER);
rectMode(CENTER);
fft = new p5.FFT(0.3);
image.filter(BLUR, 12);
noLoop();
}
function draw() {
background(0);
stroke(255);
strokeWeight(3);
noFill();
translate(width / 2, height / 2);
fft.analyze();
amp = fft.getEnergy(20, 200);
push();
if (amp > 230) {
rotate(random(-0.5, 0.5));
}
image(image, 0, 0, width + 100, height + 100);
pop();
let alpha = map(amp, 0, 255, 180, 150);
fill(0, alpha);
noStroke();
rect(0, 0, width, height);
stroke(255);
strokeWeight(3);
noFill();
let wave = fft.waveform();
for (let t = -1; t <= 1; t += 2) {
beginShape();
for (let i = 0; i <= 180; i += 0.5) {
let index = floor(map(i, 0, 180, 0, wave.length - 1));
let radius = map(wave[index], -1, 1, 150, 350);
let x = radius * sin(i) * t;
let y = radius * cos(i);
vertex(x, y);
}
endShape();
}
let particle = new Particle();
particles.push(particle);
for (let i = particles.length - 1; i >= 0; i--) {
if (!particles[i].edges()) {
particles[i].update(amp > 230);
particles[i].show();
} else particles.splice(i, 1);
}
}
function mouseClicked() {
if (sound.isPlaying()) {
sound.pause();
noLoop();
} else {
sound.play();
loop();
}
}
// Particles around the Wave
class Particle {
constructor() {
this.position = p5.Vector.random2D().mult(250);
this.velocity = createVector(0, 0);
this.acceleration = this.position.copy().mult(random(0.0001, 0.00001));
this.width = random(3, 5);
this.color = [random(200, 255), random(200, 255), random(200, 255)];
}
update(condition) {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
if (condition) {
this.position.add(this.velocity);
this.position.add(this.velocity);
this.position.add(this.velocity);
}
}
edges() {
if (
this.position.x < -width / 2 ||
this.position.x > width / 2 ||
this.position.y < -height / 2 ||
this.position.y > height / 2
)
return true;
else return false;
}
show() {
noStroke();
fill(this.color);
ellipse(this.position.x, this.position.y, this.width);
}
}
I have the following component in which I want that implementation of this sound visualization to happen:
import React from "react";
import { FormInput } from "../Forms/Form";
import "./Visualization.css";
const Visualization = () => {
// TODO: Implement p5.js to create visualization as in the linked video
const handleChange = () => {
// Play the selected audio file in that input
};
return (
<div className="visualizer-canvas-container">
<canvas id="landing-visualizer-canvas"></canvas>
<audio id="landing-audio-controls" controls />
<FormInput
accept="audio/*"
id="file-upload"
onChange={handleChange}
type="file"
/>
</div>
);
};
export default Visualization;

P5.js ellipse not following mouseX and Y

I am not sure why but the "invisible circle" is not align with the mouseX and Y and i ended up hard coding it. Anyone know why this happens?
If i just wrote (mouseX,mouseY) the ellipse will be around 300 units away from the cusor.
Is there any way fix it/ improve it?
Without setting exact value such as mouseX-300?
Thanks
*not much details i can think of for now.not much details i can think of for now.not much details i can think of for now.
let cubes = [];
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
backCol = color(243, 243, 243);
//background(backCol);
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
let xPos = map(i, 0, 9, 50, width - 50);
let yPos = map(j, 0, 9, 50, height - 50);
cubes.push(new Cubes(xPos, yPos));
}
}
}
function draw() {
background(backCol);
noFill();
for (let a = 0; a < cubes.length; a++) {
cubes[a].update();
}
}
class Cubes {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 30;
this.stroke = 70;
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
this.Invisible_Circle();
}
Invisible_Circle () {
push();
stroke(10);
//noFill();
// translate(mouseX,mouseY);
ellipse(mouseX - 280,mouseY - 280,200);
pop();
}
shape() {
push();
stroke(this.stroke);
translate(this.x - width / 2, this.y - height / 2, 0);
this.magnetic()
box(this.size);
pop();
}
shift_Color() {
let distance = dist(mouseX, mouseY, this.x, this.y);
let shiftX = map(mouseX, 0, this.a, 0, 1.0);
let change = lerpColor(this.shift1, this.shift2, shiftX);
if (distance < this.a) {
fill(change);
} else {
noFill();
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.size * 3) {
this.a = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.a);
rotateY(this.a);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
In WEBGL mode (createCanvas()) the center of the screen is by default (0, 0). Anyway, the top left coordinate of the window is still (0, 0). Hence you have to translate the mouse position, by (-width/2, -height/2):
ellipse(mouseX - width/2, mouseY - height/2, 200);
Furthermore Invisible_Circle should not a method of Cubes:
function Invisible_Circle () {
stroke(10);
//noFill();
ellipse(mouseX - width/2, mouseY - height/2, 200);
}
class Cubes {
constructor(x, y) {
// [...]
And has to be called after drawing the boxes:
function draw() {
background(backCol);
Invisible_Circle();
noFill();
for (let a = 0; a < cubes.length; a++) {
cubes[a].update();
}
Invisible_Circle();
}
See the example:
let cubes = [];
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
backCol = color(243, 243, 243);
//background(backCol);
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
let xPos = map(i, 0, 9, 50, width - 50);
let yPos = map(j, 0, 9, 50, height - 50);
cubes.push(new Cubes(xPos, yPos));
}
}
}
function draw() {
background(backCol);
Invisible_Circle();
noFill();
for (let a = 0; a < cubes.length; a++) {
cubes[a].update();
}
Invisible_Circle();
}
function Invisible_Circle () {
stroke(10);
//noFill();
ellipse(mouseX - width/2, mouseY - height/2, 200);
}
class Cubes {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 30;
this.stroke = 70;
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
}
shape() {
push();
stroke(this.stroke);
translate(this.x - width / 2, this.y - height / 2, 0);
this.magnetic()
box(this.size);
pop();
}
shift_Color() {
let distance = dist(mouseX, mouseY, this.x, this.y);
let shiftX = map(mouseX, 0, this.a, 0, 1.0);
let change = lerpColor(this.shift1, this.shift2, shiftX);
if (distance < this.a) {
fill(change);
} else {
noFill();
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.size * 3) {
this.a = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.a);
rotateY(this.a);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

Objects not shown on screen & keyPressed not working (Flappy Bird Code Challenge)

I created flappy bird game following The Coding Train's tutorial on Youtube.
(https://www.youtube.com/watch?v=cXgA1d_E-jY)
The original instruction created pipes and ball as objects, but I modified mine to be created as classes instead.
I have two problems.
1)The pipes indices I created in pipes array did not show on screen,
2)ball.goup();, which would be triggered by keyPressed() does nothing on screen, but console.log shows it is working.
How should I revise my code? Thanks.
let ball;
let pipes = [];
function setup() {
createCanvas(700, 600);
ball = new Ball();
pipes.push(new Pipe());
}
function draw() {
background(0);
ball.show();
ball.falldown();
if (frameCount % 60 == 0) {
pipes.push(new Pipe());
}
//since have splice, count from back to avoid uncounted indices
for (let i = pipes.length - 1; i >= 0; i--) {
pipes[i].show();
pipes[i].move();
if (pipes[i].hits(ball)) {
console.log("hit");
}
if (pipes[i].offscreen()) {
pipes.splice(i, 1);
}
}
}
function keyPressed() {
if (key == ' ') {
ball.goup;
console.log('up');
}
}
class Ball {
constructor(x = 50, y = height / 2, g = 0.6, v = 0, l = -20) {
this.x = x;
this.y = y;
this.gravity = g;
this.velocity = v;
this.lift = l;
}
goup() {
this.velocity += this.lift;
}
falldown() {
this.velocity += this.gravity;
this.velocity *= 0.9;
this.y += this.velocity;
if (this.y > height) {
this.y = height;
this.velocity = 0;
}
if (this.y < 0) {
this.y = 0;
this.velocity = 0;
}
}
show() {
noStroke();
fill(255);
ellipse(this.x, this.y, 25, 25);
}
}
class Pipe {
constructor(x =width, y=0, w=100, t=random(2/height), b=random(2/height), s=2, c=false) {
this.x = x;
this.y = y;
this.w = w;
this.top = t;
this.bottom = b;
this.speed = s;
this.colorchange = c;
}
hits(ball) {
if (ball.y < this.top || ball.y > height - this.bottom) {
if(ball.x > this.x && ball.x < this.x + this.w){
this.colorchange = true;
return true;
}
}
this.colorchange = false;
return false
}
move() {
this.x -= this.speed;
}
show() {
fill(255);
if (this.colorchange) {
fill(255, 0, 0);
}
rect(this.x, this.y, this.w, this.top);
rect(this.x, height - this.bottom, this.w, this.bottom);
}
//delete pipe indices from the array when pipes are off screen
offscreen() {
if (this.x < -this.w) {
return true;
} else {
return false;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
The logic is sound, but there are a couple of typos. First, the ball isn't going up because by using this.goup you're not executing the method: the parenthesis are missing. Change that line (inside the keyPressed() function) for: this.goup() and the ball will start responding to the keyboard input.
As for the pipes, they are drawing, but the default top and bottom are being defined as random(2 / height), and, being that height is equal to 600, it will result in a final value between 0 and 0.003333. To fix that, just rewrite the value assignment as t=random(height/2), b=random(height/2).
let ball;
let pipes = [];
function setup() {
createCanvas(700, 600);
ball = new Ball();
pipes.push(new Pipe());
}
function draw() {
background(0);
ball.show();
ball.falldown();
if (frameCount % 60 == 0) {
pipes.push(new Pipe());
}
//since have splice, count from back to avoid uncounted indices
for (let i = pipes.length - 1; i >= 0; i--) {
pipes[i].show();
pipes[i].move();
if (pipes[i].hits(ball)) {
//console.log("hit");
}
if (pipes[i].offscreen()) {
pipes.splice(i, 1);
}
}
}
function keyPressed() {
if (key == ' ') {
ball.goup();
//console.log('up');
}
}
class Ball {
constructor(x = 50, y = height / 2, g = 0.6, v = 0, l = -20) {
this.x = x;
this.y = y;
this.gravity = g;
this.velocity = v;
this.lift = l;
}
goup() {
this.velocity += this.lift;
}
falldown() {
this.velocity += this.gravity;
this.velocity *= 0.9;
this.y += this.velocity;
if (this.y > height) {
this.y = height;
this.velocity = 0;
}
if (this.y < 0) {
this.y = 0;
this.velocity = 0;
}
}
show() {
noStroke();
fill(255);
ellipse(this.x, this.y, 25, 25);
}
}
class Pipe {
constructor(x = width, y = 0, w = 100, t = random(height/2), b = random(height / 2), s = 2, c = false) {
this.x = x;
this.y = y;
this.w = w;
this.top = t;
this.bottom = b;
this.speed = s;
this.colorchange = c;
}
hits(ball) {
if (ball.y < this.top || ball.y > height - this.bottom) {
if (ball.x > this.x && ball.x < this.x + this.w) {
this.colorchange = true;
return true;
}
}
this.colorchange = false;
return false
}
move() {
this.x -= this.speed;
}
show() {
fill(255);
if (this.colorchange) {
fill(255, 0, 0);
}
rect(this.x, this.y, this.w, this.top)
rect(this.x, height - this.bottom, this.w, this.bottom);
}
//delete pipe indices from the array when pipes are off screen
offscreen() {
if (this.x < -this.w) {
return true;
} else {
return false;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>

p5.js How to stop rotation of certain elements

I am drawing some rotating rectangles in p5.js, and it is all working fine. Then I need some text to be displayed on top of rectangles and I want it to be motionless. However, the text is spinning as well. I have tried some solutions like setting rotate(0) or value negative to the rotation of last drawn rectangle, but nothing works.
class Rectangle {
constructor(size, colorR, colorG, colorB, angle) {
this.size = size;
this.colorR = colorR;
this.colorG = colorG;
this.colorB = colorB;
this.angle = angle;
this.actualAngle = this.angle;
}
draw() {
stroke(0);
strokeWeight(2);
fill(this.colorR, this.colorG, this.colorB);
rotate(this.actualAngle);
rect(0, 0, this.size, this.size);
}
increaseAngle() {
this.actualAngle += this.angle;
}
getActualAngle(){
return this.actualAngle;
}
}
var rectangles = [];
function setup() {
var canvas1 = createCanvas(800, 700);
canvas1.parent("regLog");
var size = 12;
for (var i = 0; i < 10; i++) {
let ranR = random(150);
let ranG = random(150);
let ranB = random(150);
let angle = random(-0.005, 0.005);
rectangles.push(new Rectangle(size, ranR, ranG, ranB, angle));
size += 50;
}
}
function draw() {
background(255);
rectMode(CENTER);
translate(width / 2, height / 2);
for (var i = rectangles.length - 1; i > 0; i--) {
rectangles[i].draw();
}
if (second() % 1 == 0) {
for (let i = 0; i < rectangles.length; i++) {
rectangles[i].increaseAngle();
}
}
rotate(0);
noStroke();
fill(255, 255, 255, 0);
rect(0, 0, 50, 50);
textAlign(CENTER);
fill(255);
textSize(60);
text("Coming soon!", 0, 0);
}
I used push() to "save" the current rotation settings and then pop() to restore them after each time you draw a rectangle. When you use rotate(), it rotates the all of the future shapes you draw, so the changes are "permanent" unless you're very careful to reset.
You can see it work in this jsfiddle -> https://jsfiddle.net/ozsLtxhb/237/
i commented out the part where you assign a parent just for the jsfiddle to work so uncomment it before you copy-paste it into anything
class Rectangle {
constructor(size, colorR, colorG, colorB, angle) {
this.size = size;
this.colorR = colorR;
this.colorG = colorG;
this.colorB = colorB;
this.angle = angle;
this.actualAngle = this.angle;
}
draw() {
/*push here*/
push();
stroke(0);
strokeWeight(2);
fill(this.colorR, this.colorG, this.colorB);
rotate(this.actualAngle);
rect(0, 0, this.size, this.size);
/*pop here*/
pop();
}
increaseAngle() {
this.actualAngle += this.angle;
}
getActualAngle(){
return this.actualAngle;
}
}
var rectangles = [];
function setup() {
var canvas1 = createCanvas(800, 700);
/* canvas1.parent("regLog"); */
var size = 12;
for (var i = 0; i < 10; i++) {
let ranR = random(150);
let ranG = random(150);
let ranB = random(150);
let angle = random(-0.005, 0.005);
rectangles.push(new Rectangle(size, ranR, ranG, ranB, angle));
size += 50;
}
}
function draw() {
background(255);
rectMode(CENTER);
translate(width / 2, height / 2);
for (var i = rectangles.length - 1; i > 0; i--) {
rectangles[i].draw();
}
if (second() % 1 == 0) {
for (let i = 0; i < rectangles.length; i++) {
rectangles[i].increaseAngle();
}
}
rotate(0);
noStroke();
fill(255, 255, 255, 0);
rect(0, 0, 50, 50);
textAlign(CENTER);
fill(255);
textSize(60);
text("Coming soon!", 0, 0);
}

My gravity simulation gets stuck

I am making a gravity simulator to get the feel for physics based coding and I have made an idea here. But I have a problem, after some point after bouncing, the particle (square) gets stuck bouncing to the same point. Does anyone know why?
Heres a link to the jsfiddle: https://jsfiddle.net/jjndeokk/6/
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var gravity, objectDensity, force;
gravity = 10.8;
function Object(mass, x, y, w, h, acc, hacc) {
this.m = mass;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.a = acc;
this.ha = hacc;
};
var particle = [];
var rows = [1];
for (let i = 0, len = rows.length; i < len; i++) {
particle.push(new Object(10, i * 30, 10, 20, 20, 0, 0));
};
function draw() {
ctx.clearRect(0, 0, c.width, c.height)
for (let i = 0, len = particle.length; i < len; i++) {
ctx.fillRect(particle[i].x, particle[i].y, particle[i].w, particle[i].h)
particle[i].a += gravity;
particle[i].ha = 3;
particle[i].x += particle[i].ha;
if (particle[i].y + particle[i].h + particle[i].a > c.height) {
particle[i].y = c.height - particle[i].h;
} else {
particle[i].y += particle[i].a;
}
}
}
function update() {
for (let i = 0, len = particle.length; i < len; i++) {
if (particle[i].a >= 0) {
if (particle[i].y + particle[i].h >= c.height) {
particle[i].a *= -1;
}
}
}
draw();
}
setInterval(update, 60);
The main reason your bounce gets stuck is that you are applying gravity to the dot even when it is on the ground. After that, you reverse its velocity and it flies back up into the air.
You need to check whether it's on the ground and not apply gravity if it is:
if (isAboveFloor(particle)) {
particle.a += gravity;
}
Once that's fixed, what you'll actually find is that the bounce goes back and forth between its initial height and the ground, and this is to be expected - it's conservation of momentum.
In order to make the bounce more realistic, you need to introduce a "coefficient of restitution" that is less than 1:
if (particle.y + particle.h >= c.height) {
particle.a *= -cRest; // cRest is between 0 and 1
}
Once that's done, you get a pretty nice simulation: https://jsfiddle.net/jjndeokk/17/
I've also made the following modifications:
Used .forEach so that the code isn't completely littered with [i]s
Made the gravity and velocity calculations take time into account
Renamed particle.a and particle.ha to particle.vy and particle.vx because those properties were measuring velocity, not acceleration.
Moved all of the calculations into the update() function so you don't have most of them in the draw() function.
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var gravity, objectDensity, force;
gravity = 240; // pixels / second / second
var cRest = 0.6;
var interval = 60;
var secondsPerInterval = interval / 1000;
function Object(mass, x, y, w, h, vxi, vyi) {
this.m = mass;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vxi;
this.vy = vyi;
};
var particles = [];
var rows = [1];
for (let i = 0, len = rows.length; i < len; i++) {
particles.push(new Object(10, i * 30, 10, 20, 20, 40, 0));
};
function draw() {
ctx.clearRect(0, 0, c.width, c.height);
particles.forEach(function(particle) {
ctx.fillRect(particle.x, particle.y, particle.w, particle.h);
})
}
function isAboveFloor(particle) {
return Math.abs(particle.y + particle.h - c.height) > 1;
}
function update() {
particles.forEach(function(particle) {
if (particle.vy < 0 || isAboveFloor(particle)) {
particle.x += particle.vx * secondsPerInterval;
particle.y = Math.min(particle.y + particle.vy * secondsPerInterval, c.height - particle.h);
// if still above floor, accelerate
if(isAboveFloor(particle)){
particle.vy += gravity * secondsPerInterval;
}
}
if (particle.vy >= 0 && particle.y + particle.h >= c.height) {
particle.vy *= -cRest;
}
console.log(particle);
});
draw();
}
setInterval(update, interval);
<canvas id="canvas" height="600" width="800"></canvas>

Categories