I don't know why my particles won't show up on the screen, when I play the sketch everything is frozen in place and particles don't come out. Particles are supposed to come out from the circles, while the circle is an audio visualizer of the song. I'm following a tutorial and making a few tweaks on my own so I can actually learn the code, but I'm stuck on showing the particles on the screen.
const { Part } = require("c:/users/12403/.vscode/extensions/samplavigne.p5-vscode-1.2.8/p5types");
var song
var fft
var img
var particles = []
function preload(){
song = loadSound('Clair de Lune With Rain.mp3')
img = loadImage('rain.jpg')
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES)
imageMode(CENTER)
img.filter(BLUR, 2)
fft = new p5.FFT()
}
function draw() {
background(0)
stroke(255)
strokeWeight(3)
noFill()
translate(width/2, height/2)
image(img, 0, 0, width, height)
fft.analyze()
amp = fft.getEnergy(20,200)
var wave = fft.waveform()
for (var t = -1; t <= 1; t += 2){
beginShape()
for(var i = 0; i <= 180; i+= 0.3){
var index = floor (map(i, 0, width, 0 , wave.length-1))
var r = map(wave[index], -1, 1, 150, 350)
var x = r*sin(i) * t
var y = r*cos(i)
vertex(x,y)
}
endShape()
}
var p = new Particle()
particles.push(p)
for(var i=0; i<particles.length; i++){
particles[i].show()
}
}
function mouseClicked(){
if (song.isPlaying()){
song.pause()
}
else{
song.play()
loop()
}
}
class Particle{
constructor(){
this.pos = p5.Vector.random2D().mult(250)
}
show(){
noStroke()
fill(random)
ellipse(this.pos.x, this.pos.y, 4)
}
}
Your code was crashing because this is invalid:
fill(random)
The fill function expects a color or number, and random is a function, so you need to invoke the random function to get a numeric value. Something like fill(random(0, 255)).
With that fixed your sketch runs and draws the particles, but there is clearly some logic missing:
you create one new particle per frame
each new particle gets a random position around a circle with a radius of 200
each particle's position has nothing to do with the audio and never changes over time
you never remove particles
You question is much to vague to figure out what you actually want to have happen, and you clearly need to take a few more steps to make it happen (such as setting each particle's position according to the audio FFT values, or updating each particle's position each frame). Also You definitely need a way to limit the number of particles, otherwise your sketch will eventually grind to a halt.
var song
var fft
var img
var particles = []
function preload() {
// Audio licensed Creative Commons Attribution Non-Commercial
// by apoxode https://apoxode.bandcamp.com/releases
// http://dig.ccmixter.org/files/Apoxode/63948
song = loadSound('https://www.paulwheeler.us/files/Apoxode_-_halcyon.mp3');
img = loadImage('https://www.paulwheeler.us/files/windows-95-desktop-background.jpg');
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES)
imageMode(CENTER)
img.filter(BLUR, 2)
fft = new p5.FFT()
}
function draw() {
background(0)
stroke(255)
strokeWeight(3)
noFill()
translate(width / 2, height / 2)
image(img, 0, 0, width, height)
fft.analyze()
amp = fft.getEnergy(20, 200)
var wave = fft.waveform()
for (var t = -1; t <= 1; t += 2) {
beginShape()
for (var i = 0; i <= 180; i += 0.3) {
var index = floor(map(i, 0, width, 0, wave.length - 1))
var r = map(wave[index], -1, 1, 150, 350)
var x = r * sin(i) * t
var y = r * cos(i)
vertex(x, y)
}
endShape()
}
var p = new Particle()
particles.push(p)
for (var i = 0; i < particles.length; i++) {
particles[i].show()
}
}
function mouseClicked() {
if (song.isPlaying()) {
song.pause()
} else {
song.play()
loop()
}
}
class Particle {
constructor() {
this.pos = p5.Vector.random2D().mult(250)
}
show() {
noStroke()
fill(random())
ellipse(this.pos.x, this.pos.y, 4)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
Related
This is the code
let x = 10;
let y = 0;
let bottomy = 100;
let Speed = 1
function setup() {
createCanvas(windowWidth,600);
}
function draw() {
background(0)
strokeWeight(3)
stroke(255)
for (i = 0; i < width; i += 20) {
water()
line(i,y,i,bottomy)
}
bottomy = bottomy + Speed;
if (bottomy > height) {
bottomy = 100
}
frameRate(1)
}
function water(){
bottomy = random(0,600)
//noLoop()
}
I want to randomise each y2 line coordinate in the for loop. But then have the y2 line coordinate to increment by 1. To create a rain effect.
I can't put the random variable in setup and then call it in the for loop because it won't affect each line in the for loop and I can't put the for loop in setup because I need the line to be drawn.
I've also tried creating a function that loops once and then calling it in draw but it ends up stopping all the code in the draw function.
I've seen examples where they generate like an infinite amount of random lines. But I would like to keep the x position of each line the same if possible. If it's not possible to do this with a for loop and I have to draw each line individually that's fine I was just wondering if this is possible to efficiently do this with a for loop.
I think what you are looking for is individual variables for each line.
Probably a classic:
from-several-variables-to-arrays
from-several-arrays-to-classes
situation. (those were made in Java's processing, but the concept can be easily adapted)
Anyway, i think this is what you tried to make with your code, but it does not work as intended, since you only have one bottomY var for all lines.
let x = 10;
let y = 0;
let bottomY = 100;
let spd = 1; //by convention capitalized names are for classes
function setup() {
createCanvas(windowWidth, 600);
}
function draw() {
background(0);
strokeWeight(3);
stroke(255);
for (i = 0; i < width; i += 20) {
if (bottomY > height) {
bottomY = random(600);
}
line(i, y, i, bottomY);
}
bottomY += spd;
}
What you want is several lines that each has individual x and bottomY
So you could use two arrays for that:
let x = [];
let y = 0;
let bottomY = [];
//why not have individual speeds as well...
let spd = [];
function setup() {
createCanvas(windowWidth, 600);
for (i = 0; i < width; i += 20) {
x.push(i);
bottomY.push(random(height));
spd.push(random(0.6, 2));
}
strokeWeight(3);
stroke(255);
}
function draw() {
background(0);
for (let i = 0; i < x.length; i++) {
line(x[i], y, x[i], bottomY[i]);
if (bottomY[i] < height) {
bottomY[i] += spd[i];
} else {
bottomY[i] = random(height);
}
}
}
And finally a simple class implementation:
let rain = [];
function setup() {
createCanvas(windowWidth, 600);
for (i = 0; i < width; i += 20) {
rain.push(new Drop(i, 0));
}
}
function draw() {
background(0);
for (const d of rain) {
d.run();
}
}
class Drop {
constructor(x, y) {
this.x = x;
this.y = y;
this.btY = random(height);
this.spd = random(0.6, 2);
}
run() {
strokeWeight(3);
stroke(255);
line(this.x, this.y, this.x, this.btY);
if (this.btY < height) {
this.btY += this.spd;
} else {
this.btY = random(height);
}
}
} //class
Did it make sense?
I am trying to implement a group of objects that will fall from the top of the canvas in random spots, and then when they hit the bottom of the canvas they respawn at the top of the canvas. Ideally, I want to change this code in the future to use sprites that will fall from the top of the canvas and explode when they hit the bottom of the canvas but then respawn again at the top.
I have the bellow code that works when the mouse is pressed but I would like to rewrite this code so the event happens automatically without the mouse needed to be pressed to make the objects fall.
see code below.
var x, y;
var particle = [];
function setup() {
createCanvas(720, 400);
// Starts in the middle
x = width / 2;
y = height;
}
function draw() {
background(0);
if (mouseIsPressed){
stroke(50);
fill(100);
ellipse(x, y, 24, 24);
}
x = x + 0;
// Moving up at a constant speed
y = y + 2;
// Reset to the bottom
if (y >= height) {
y = 0;
}
}
Use the mousePressed() to add new particles to the list. The particles are added at the current mouse position (mouseX, mouseY). Move and draw the particles in a loop:
var particles = [];
function setup() {
createCanvas(720, 400);
}
function mousePressed() {
particles.push([mouseX, mouseY]);
}
function draw() {
background(0);
stroke(50);
for (let i=0; i < particles.length; i++) {
particles[i][1] += 2;
if (particles[i][1] > height) {
particles[i][1] = 0;
}
fill(100);
ellipse(particles[i][0], particles[i][1], 24, 24);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Alternatively, you can spawn the particles with a time interval. Get the number of milliseconds (thousandths of a second) since starting the sketch with millis(). The y-coordinate of a new particle is 0 and the x-coordinate is random():
var particles = [];
function setup() {
createCanvas(720, 400);
}
function mousePressed() {
particles.push([mouseX, mouseY]);
}
function draw() {
let max_no_of_particles = 40;
let expected_no_of_praticles = min(max_no_of_particles, millis() / 100);
if (particles.length < expected_no_of_praticles) {
particles.push([random(12, width-12), 0]);
}
background(0);
stroke(50);
for (let i=0; i < particles.length; i++) {
particles[i][1] += 2;
if (particles[i][1] > height) {
particles[i][1] = 0;
}
fill(100);
ellipse(particles[i][0], particles[i][1], 24, 24);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
I am trying to assign a random color to each circle once, and then get that color to progress through the color spectrum. I am a beginner and cannot work out how to stop the random color in draw function from resetting every loop without pulling it into setup function which then makes all the circles start from the same random color. Code below of what I have so far. It also seems to be changing the colors in a slight wave at the moment, I assume this is to do with the 'for'. All i want is a grid of circles that are assigned a random color once and then go on to progress their color through the color wheel at the same speed. Thanks in advance!
let intervals = [10];
let count;
let frameWidth;
let distance;
let diam1;
let col1;
let sat1;
let bri1;
let speed;
function setup() {
createCanvas(800, 800);
colorMode(HSB, 360, 100, 100, 1);
//initiate draw variables
count = random(intervals);
frameWidth = (width*0.8);
distance = (frameWidth/count);
col1 = random(360);
diam1 = distance*0.5;
speed = 0.005;
}
function draw() {
background(0, 0, 0, 1);
// draw grid of circles
for (let x = width * 0.1; x <= width * 0.9; x += distance) {
for (let y = height * 0.1; y <= height * 0.9; y += distance) {
sat1 = 100;
bri1 = 100;
noStroke();
fill(col1, sat1, bri1, 1);
ellipse(x,y,diam1);
col1 = col1 + speed
if (col1 >= 360) {
col1 = 0
}
}
}
}
You have to create a grid of random colors in setup:
let speed, colors, rows, columns;
function setup() {
// [...]
columns = Math.round(frameWidth / distance) + 1;
rows = Math.round(frameWidth / distance) + 1;
colors = [];
for (let i = 0; i < columns; i ++) {
colors.push([]);
for (let j = 0; j < rows; j ++) {
colors[i].push(random(360));
}
}
speed = 1;
}
Use the the colors in the draw function. Complete example:
let intervals = [10];
let count, frameWidth, distance;
let sat1, bri1;
let speed, colors, rows, columns, diameters;
function setup() {
createCanvas(800, 800);
colorMode(HSB, 360, 100, 100, 1);
//initiate draw variables
count = random(intervals);
frameWidth = (width*0.8);
distance = (frameWidth/count);
columns = Math.round(frameWidth / distance) + 1;
rows = Math.round(frameWidth / distance) + 1;
colors = [];
diameters = []
for (let i = 0; i < columns; i ++) {
colors.push([]);
diameters.push([]);
for (let j = 0; j < rows; j ++) {
colors[i].push(random(360));
diameters[i].push(random(TWO_PI));
}
}
speed = 1;
}
function draw() {
background(0, 0, 0, 1);
// draw grid of circles
for (let i = 0; i < columns; i ++) {
for (let j = 0; j < rows; j ++) {
sat1 = 100;
bri1 = 100;
noStroke();
fill(colors[i][j], sat1, bri1, 1);
let d = distance * (0.5 + 0.4 * sin(diameters[i][j]));
ellipse(width * 0.1 + i * distance, height * 0.1 + j * distance, d);
colors[i][j] = (colors[i][j] + speed) % 360;
diameters[i][j] = diameters[i][j] + 0.05;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
The ui will be a 2d grid of circles.
The data structure will be a 2d array of colour values. The data structure never needs to be updated; to get a circle's current colour we only need to know its initial colour, and the amount of time that has passed!
I'm going to use the hsla() css colour directive to define the colours, as it makes shifting the hue trivial.
The colourShiftingCircles function takes a canvas context and some display parameters, and will maintain an animating (colour-shifting) grid of circles on the provided canvas context.
The function signature is:
colourShiftingCircles({ ctx, rad, numX, numY, saturation=100, lightness=50, huePerSec=30 })
ctx: the canvas context
rad: circle radius in pixels
numX: number of circles across
numY: number of circles down (total of numX x numY circles)
saturation: control how "colourful" or "nongrey" the colours are
lightness: control how shifted towards black/white the colours are
huePerSec: how quickly to cycle hues
let canvas = document.querySelector('canvas');
// This just ensures the canvas is a reasonable size; it's optional
let resize = () => {
let { width, height } = canvas.parentNode.getBoundingClientRect();
if (width.toString() !== canvas.getAttribute('width')) canvas.setAttribute('width', width);
if (height.toString() !== canvas.getAttribute('height')) canvas.setAttribute('height', height);
};
resize();
window.addEventListener('resize', resize);
let colourShiftingCircles = ({ ctx, rad, numX, numY, saturation=100, lightness=50, huePerSec=30 }) => {
// This value allows the animation to be ended at some point in the future
let control = { run: true, end: () => control.run = false };
// Animation loop runs inside an `async` function:
(async () => {
// An array of size `numX` x `numY` items, defining initial hue values for circles
let data = [ ...new Array(numX) ].map(() => [ ...new Array(numY) ].map(() => {
return { hue: Math.random() * 360 };
}));
// Mark the start time; this will allow us to tell how much time has passed later
let startTime = performance.now();
while (control.run) {
// Wait for animation frame; this is a basic principle of canvas animations
await new Promise(r => requestAnimationFrame(r));
// Figure out how much the hue has shifted by at this point in time
let totalHueShift = ((performance.now() - startTime) / 1000) * huePerSec;
// Draw every circle:
for (let y = 0; y < numY; y++) { for (let x = 0; x < numX; x++) {
// Calculate the hue based on the circle's initial colour, and the amount
// of hue shifted due to passage of time
let hue = data[x][y].hue + totalHueShift;
// Simply draw a circle with an hsla fill colour
ctx.beginPath();
ctx.fillStyle = `hsla(${Math.floor(hue) % 360}deg ${saturation}% ${lightness}%)`;
ctx.arc((rad * 2 * x) + rad, (rad * 2 * y) + rad, rad, 0, Math.PI * 2);
ctx.fill();
}}
}
})();
return control;
};
let { end } = colourShiftingCircles({ ctx: canvas.getContext('2d'), rad: 20, numX: 5, numY: 5 });
// If you ever want to end the animation just call `end()`!
/* Just getting the canvas to display in a reasonable way; this is optional */
html, body, canvas { position: absolute; width: 100%; height: 100%; margin: 0; padding: 0; }
canvas { outline: 1px solid black; }
<canvas></canvas>
I was trying to draw while moving the circle when we press the mouse. But eventually, when I move it after some time the rotating circle and the Big circle are getting slowed.
I don't know what is the reason, I was thinking it because the list is accumulated with the coordinates and making the code lag eventually please help.
If you press the mouse and wait for some time you can observe the circles getting slowed eventually. correct me if I am wrong.
class Particle{
constructor(){
this.center = createVector(0,0);
this.radius = 20;
this.theta = 0;
this.line = createVector(0,0);
this.history = [];
this.velocity = createVector();
}
render(){
translate(60,60);
circle(this.center.x,this.center.y,this.radius);
circle(this.line.x+this.center.x, this.line.y+this.center.y,10);
beginShape();
for(let i=0;i < this.history.length; i++){
let pos = this.history[i];
noFill();
vertex(pos.x, pos.y);
endShape();
}
}
update(){
this.line.x = this.radius*cos(this.theta);
this.line.y = this.radius*sin(this.theta);
if (mouseIsPressed){
this.center.x = (1-0.025)*this.center.x + 0.025*(this.line.x + this.center.x);
this.center.y = (1-0.025)*this.center.y + 0.025*(this.line.y + this.center.y);
let v = createVector(this.center.x, this.center.y);
this.history.push(v);
} else{
this.theta += 0.01;
}
}
}
let particle;
function setup() {
createCanvas(windowWidth, windowHeight);
particle = new Particle();
}
function draw() {
background(220);
particle.render();
particle.update();
}
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.js"></script>
</body>
</html>
There is a misalignment of beginShape() and endShape() in your code. beginShape() is before the loop, but endShape() is in the loop. Move endShape() after the loop:
beginShape();
for(let i=0;i < this.history.length; i++){
let pos = this.history[i];
noFill();
vertex(pos.x, pos.y);
}
endShape();
Very likely the code is lagging, because the number of points in the history increase. Try to reduce the number of points. Pixels can only be display on integral positions. Avoid adding duplicate items to the list:
update(){
this.line.x = this.radius*cos(this.theta);
this.line.y = this.radius*sin(this.theta);
if (mouseIsPressed){
this.center.x = (1-0.025)*this.center.x + 0.025*(this.line.x + this.center.x);
this.center.y = (1-0.025)*this.center.y + 0.025*(this.line.y + this.center.y);
let v = createVector(this.center.x, this.center.y);
let h = this.history;
if (h.length == 0 ||
Math.trunc(h[h.length-1].x) != Math.trunc(v.x) ||
Math.trunc(h[h.length-1].y) != Math.trunc(v.y)) {
this.history.push(v);
}
} else{
this.theta += 0.01;
}
}
If the application is still lagging, you can try drawing the dots on a p5.Renderer object the size of the canvas (createGraphics()). Draw this object on the canvas like a background image.
I have this code that stacks canvas particles in a lane and then resets the whole canvas when one lane is full.
It does this by setting "particles = []"
I want to modify this behavior by only resetting the canvas for that particular lane. Is it possible?
$(document).ready(function () {
"use strict";
var c = document.getElementById("c"),
ctx = c.getContext("2d"),
WIDTH = c.width = window.innerWidth,
HEIGHT = c.height = window.innerHeight;
var particles = [],
particle = null,
particleCount = 3,
radius = 0,
numParticles = [0, 0, 0, 0, 0, 0],
colors = ["#00FF6D", "E8D90C", "#FF5900", "#C00CE8", "#0D90FF"];
var Vector = function (x, y) {
this.x = x || 0;
this.y = y || 0;
};
Vector.prototype = {
constructor: Vector,
add: function (v) {
this.x += v.x;
this.y += v.y;
},
sub: function (v) {
this.x -= v.x;
this.y -= v.y;
},
mul: function (v) {
this.x *= v.x;
this.y *= v.y;
}
};
var Particle = function (position, velocity, radius, lane, color) {
this.position = position;
this.velocity = velocity;
this.radius = radius;
this.baseRadius = radius;
this.angle = 3;
this.lane = lane;
this.color = color;
};
Particle.prototype = {
constructor: Particle,
update: function (lane) {
this.radius = 3;
this.angle += 10;
this.position.add(this.velocity);
// CHECKS FIRST IF THERE'S ALREADY A PARTICLE IN THE LANE AND THEN SHORTENS THE STOP LENGTH
if (this.position.x > WIDTH - (numParticles[this.lane] + 1) * 120) {
// IF THERE IS ALREADY A PARTICLE ON A LANE THE NUMBER OF PARTICLES PER IS INCREASED
if (this.velocity.x > 0) {
console.log(numParticles[this.lane] + " particles in this lane")
numParticles[this.lane]++;
if (numParticles[this.lane] > 8) {
numParticles[this.lane] = 0;
particles = [];
}
// STOPS THE PARTICLE
this.velocity.x = 0;
}
}
},
render: function (ctx) {
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 5; j++) {
ctx.beginPath()
ctx.fillStyle = this.color;
ctx.arc(this.position.x - i * 12, (this.position.y - 30) + j * 12, this.radius + 0.5, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
}
};
function addParticle(lane) {
radius = 3;
particle = new Particle(
new Vector(-radius, lane * (HEIGHT) / 5),
new Vector(5),
radius,
lane,
colors[Math.round(Math.random() * 4)]
);
particles.push(particle);
}
requestAnimationFrame(function loop() {
requestAnimationFrame(loop);
ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0, len = particles.length; i < len; i++) {
particle = particles[i];
particle.update();
particle.render(ctx);
}
});
//
// ─── SOCKETIO CONNECTION TO NODE WEBSERVER RUNNING OPENCV ───────────────────────
//
var socket = io.connect('http://localhost:8000');
// SEND "CONNECTED" MESSAGE ONCE SUCCSEFULLY CONNECTED
socket.on('connect', function () {
console.log('connected');
});
socket.on('message', function (msg) {
console.log(msg);
// IF NUMBER OF CARS DETECTED IS MORE THAN 0, A PARTICLE WILL BE ADDED RANDOMLY TO ONE OF THE 4 LANES
if (msg >= 0) {
addParticle(Math.round((Math.random() * 3) + 1));
}
});});
Filtering arrays
If you have an array of particles and want to remove some items that satisfy some condition, there are a few ways to do it.
Array.filter
Create a new array using Array.filter
var particles = []; // fill with particles
// remove particles in lane 3 using arrow function
particles = particles.filter(particle => particle.lane !== 3);
// or using via function
particles = particles.filter(function(particle){ return particle.lane !== 3});
This method does create a new array so incurs some penalties due to GC (Garbage collection) and allocation overheads. Also if you have more than one referance to the array you need to up date all references.
var particles = [];
const obj = {
particles : particles, // referance to the array particles.
}
// remove particles in lane 3
particles = particles.filter(particle => particle.lane !== 3)
// the array referenced by obj.particles still has the unfiltered array
obj.particles = particles; // update reference
Array.splice to filter
When there is need to performance you can use Array.splice to remove items from the array. It also has the advantage of not creating a new referance that you would have to propagate.
for(var i = 0; i <particles.length; i++){ // must use particles.length
if(particles[i].lane === 3){
particles.splice(i--,1); // decrease i so you do not skip an item
// as slice will move the index of all
// items above i down one
}
}
// do not use a variable to store the length in the for loop
var len = particles.length
for(var i = 0; i < len; i++){ // will cause unexpected behaviors because
// the array length will change
Flag active
For the best performance you would assign a flag to all particles that determines if the particle is updated and rendered.
for(var i = 0; i <particles.length; i++){
particles[i].active = ! particles[i].lane === 3;
}
Before you update and render you would check if active is true
// in render and update loops
for(var i = 0; i <particles.length; i++){
if(particles[i].active){
particles[i].update(); // or render()
}
}
There is of course the fact you have a growing array. To stop the uncontroled growth of the array you change the way you set the particle properties adding a method to the particle object.
function Particle(){}; // constructor
Particle.prototype.set = function(pos,vel,foo,bar){
this.active = true;
this.velocity = vel;
//... and the other properties
}
The when you add a particle you first check if there is an inactive particle in the particles array and use that by setting its properties with the set function. If there are no inactive particles then you just create a new particle and add it to the particles array setting its properties with set.
function addParticle(pos,vel,foo,bar){
var i;
for(i = 0; i < particles.length; i++){
if(! particles[i].active){
particles[i].set(pos,vel,foo,bar);
return;
}
}
// no inactive particles create a new one
particles.push(
new Particle().set(pos,vel,foo,bar);
);
}