P5.js has it's own loop, which calls the draw() function FPS amount times per second. However, there is a function noLoop() which can be placed in the setup()function, which disables P5.js own loop.
So I made my own loop (this is necessary for my project), which looks like follows:
customLoop = function(){
while(somethingIsTrue){
//custom code here
draw();
}
}
So I expect to see the canvas updated quite fast as there is no FPS set. However, the canvas is only redrawn again AFTER the customLoop is done.
Short in short: the canvas doesn't update while the custom loop is still running, it will only show the updates after the custom loop is done.
How can I update the canvas in my own loop continiously?
People have asked for more context, so i'll try my best to explain. I have made my own JS library that makes it easy to create genetic algorithms for neural networks.
The genetical algorithm in my library is set up as follows:
var evol = new Evolution({
size: 50,
mutationRate: 0.05,
networkSize: [4,10,1],
mutationMethod: [Mutate.MODIFY_RANDOM_BIAS, Mutate.MODIFY_RANDOM_WEIGHT],
crossOverMethod: [Crossover.UNIFORM, Crossover.AVERAGE],
selectionMethod: [Selection.FITNESS_PROPORTIONATE],
elitism: 5,
fitnessFunction: function(network){
//something here that computes for every network, AT THE SAME TIME showing it LIVE for the user to watch
}
});
Pay attention to the comment in the fitnessFunction. Then I run a genetic algorithm loop as follows:
var notFinished = true;
while(notFinished){
evol.evaluate();
if(evol.getAverage() > 2000){
notFinished = false;
console.log('done');
}
evol.select();
evol.crossOver();
evol.mutate();
evol.replace();
};
So as you see, my code continously runs the genetical algorithm. But the whole problem lies in the fitness function. This specific genetical algorithm is made to train neural networks how to play snake. So in the fitness function, i'm actually running the neural network until the snake dies. However, for every snake that is tested, I want the user to see how the neural networks are doing. So my fitness function looks something as follows:
fitnessFunction = function(network){
while(snakeIsNotDead){
computeNeuralNetworkOutput();
giveSnakeNewMovementDirectionBasedOnOutput();
// most importantly
redrawTheSnakeWithNewPosition(); // which is redraw() with P5.js
}
So for every generation that is tested, I want to see all the 50 snakes performing. However, all I see after the network is done is the last position the snake had in the last snake evaluation.
Please try to post your code as an MCVE that we can actually run. Here's a small example that shows your problem:
function setup(){
createCanvas(500, 500);
for(var i = 0; i < 600; i ++){
redraw();
}
}
function draw(){
ellipse(random(width), random(height), 20, 20);
}
This will show a canvas with a bunch of random circles already drawn on it. I gather that what you want is to see the circles accumulate, like you're "fast-forwarding" the sketch.
Using your own loop is a little fishy. Processing already has its own looping mechanism, and it contains logic for double-buffering the canvas. That's why you don't see anything until the drawing is completed. You could just rely on Processing's built-in timing mechanisms. Here's an example:
function setup(){
createCanvas(500, 500);
frameRate(120);
}
function draw(){
if(frameCount == 600){
frameRate(60);
}
ellipse(random(width), random(height), 20, 20);
}
Note that this might not work if the device you're running limits the framerate. But the point is that you shouldn't need to create your own loop. Just use Processing's built-in looping logic.
Edit: Like I said, the reason you aren't seeing any of your frames is because Processing is double-buffered. That means Processing will wait until it goes through all events and draws everything to the screen. This is an oversimplification, but you can think of the above loop as adding 600 redraw events to the event queue. Processing goes through them, but doesn't actually show the result until it's done.
You can work around this by calling each redraw after this event queue is emptied out. One slightly hackish way to do that is using the setTimeout() function:
function setup(){
createCanvas(500, 500);
for(var i = 0; i < 600; i ++){
setTimeout(redraw, 0);
}
}
function draw(){
ellipse(random(width), random(height), 20, 20);
}
Now instead of calling the redraw() function 600 times in a row, the code adds 600 timeout events to the event queue. Each one of them will be processed individually, so you see the intermittent frames.
I will say that this still seems like a hacky way to accomplish your goal. If I were you I would still try to separate the draw() function from your actual logic. Have your simulation set variables, and have the draw() function use those variables. That's the "correct" way to solve this problem. Using the setTimeout() function will work for now, but my guess is that it'll cause problems for you down the road.
Based on the documentation, you want to avoid calling "draw()" directly. Try something like:
customLoop = function(){
while(somethingIsTrue){
//custom code here
redraw(); // Causes the code inside "draw()" to execute once
}
}
https://p5js.org/reference/#/p5/draw
Specifically: draw() is called automatically and should never be called explicitly. It should always be controlled with noLoop(), redraw() and loop().
In addition, since JavaScript is single threaded, so long as the while loop is ticking away, nothing else can execute. Here's a related article:
Asynchronous function in a while-loop
And a nice, thorough explanation:
http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/
You could try something like:
var timerId = 0;
timerId = setTimeout(myCustomRedraw(), 10); // Every 10 milliseconds.
myCustomRedraw = function () {
// custom code here
if (<stop loop condition>) {
clearTimeout(timer); // Will stop the timeout loop.
}
redraw()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With