Why canvas drawing is asynchronous, but API is synchronous? - javascript

I am writing a vizualization of neural network, and I would like to redraw it on each training iteration, so I have next button with onclick callback:
startButton.onclick = () => {
for (let i = 0; i < trainData.length; i++) {
setTimeout(() => {
network.trainSample(trainData[i])
ctx.clearRect(0, 0, width, height)
drawNN(network)
}, 0)
}
}
The problem is, if I take off setTimeout, it will execute all the training, and redraw everything in the end.
As far as I know, there is an event loop and what setTimeout trick does, it creates a Job in a event queue that will be executed not exactly now, but as soon as possible.
Okay, but if canvas drawing is asynchronous and drawing get's postponed till the end, why it's api is synchronous?
Minimal example:
const canv = document.getElementById('myCanv')
const ctx = canv.getContext('2d')
ctx.strokeStyle = '#000000'
for (let x = 0; x <= 100; x++) {
ctx.clearRect(0, 0, 100, 100)
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(x, 100)
ctx.stroke()
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas width="100" height="100" id="myCanv"></canvas>
</body>
</html>

Your usage is incorrect and even if canvas drawing was synchronous you most likely would see only the last frame anyway with some way too fast weird animation in between. What you need is instead of standard loop use some sort of animation loop. For example:
let i = 0;
function animationLoop() {
if (i < trainData.length) {
network.trainSample(trainData[i]);
ctx.clearRect(0, 0, width, height);
drawNN(network);
i++;
requestAnimationFrame(animationLoop);
}
}
requestAnimationFrame(animationLoop);
Here I am using requestAnimationFrame which would result in around 60 frames per second. My guess for your case this might still be too fast. You can limit frames per second using additional setTimeout inside animateLoop function.

Related

Is parallelism doable with JavaScript?

I am trying to get a better performance on my JavaScript-function. I draw with myPopulation=50.000 dots on a canvas and it takes apros 230ms. As I have this in another loop with a frequence of 100ms, I get a delay due to the below function.
function drawThis(intervals) {
ctx.clearRect(0, 0, myWidth, myHeight);
ctx.beginPath();
for (var i = 0; i < myPopulation; i++) {
ctx.arc(persons[i].posX, persons[i].posY, 2, 0, 2 * Math.PI);
if (persons[i].infected) {
ctx.fillStyle = "#FF0000";
}
else {
ctx.fillStyle = "#000000";
}
if (i < myPopulation - 1) {
ctx.moveTo(persons[i + 1].posX, persons[i + 1].posY);
}
}
ctx.fill();
My idea was to divide myPopulation in equal intervals with a helper function like
var myIntervals = divideRangeInNParts(0, myPopulation, intervals);
and to them parallel. So the pseudo-code would be:
divide myPopulation in equal parts
drawThis(interval[0][0], interval[0][1]);
drawThis(interval[1][0], interval[1][1]);
drawThis(interval[2][0], interval[2][1]);
drawThis(interval[3][0], interval[3][1]);
[...]
wait for all of them to finish
continue
I have read a lot about JavaScript as a single-threaded language, Promises, Web Workders etc., but I could not find any solution which suits my problem.
Any idea or hint?
I am new here and if there is any problem with my question, please tell me also.
Thanks in advance

"while" loop to change interval of "setTimeout" NOT WORKING

I created a rectangle , and i want to give it a speed that change over time (acceleration) .
So I made a "setTimeout" inside a "while" loop.
Supposedly , the "while" loop should continuously change the interval of the "setTimeout" (var=interval) by -1 , but instead it replaces it with 1 !!, wich makes the rectangle print every 1 milliseconds .
I would like to know why this happens .
the same thing happens if i use the "for" loop.
and i wouldn't mind any other alternative to create acceleration effect.
thank you
var canvas = document.getElementById("canvas")
var context = canvas.getContext("2d")
var posX=20;
var posY=20;
var interval = 500;
function print () {
//background
context.fillStyle="black";
context.fillRect(0, 0, 500, 500);
//object
context.fillStyle="#4286f4";
context.fillRect(posX, posY, 50, 50);
posX = posX + 1;
posY = posY + 1;
}
while (interval > 300) {
interval-- ;
setTimeout(print, interval);
}
<!DOCTYPE html>
<html>
<head>
<title>Particles</title>
<meta charset="UTF-8"/>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
</body>
</html>
the "while" loop should continuously change the interval of the "setTimeout"
No, it schedules a new timer at the new interval. The previous one keeps running as well. You very, very, very quickly end up with a bunch of timers pending -- which all then expire one right after the next.
Once you start a timer, you can't change when it fires. You can cancel it, but you can't change when it fires.
setTimeout also schedules a single timed callback. If you want repeated ones, use setInterval or schedule a new callback from the setTimeout callback when it runs.
I recommend taking a step back and experimenting with the basics of timers and intervals before moving on to something complex like doing animations.
Separately: setTimeout and setInterval are the wrong tools for animation, at least in isolation. Instead, when you know you need to update the circle, use requestAnimationFrame to have the browser call you immediately before it renders the display (it will fire ~60 times/second, so only request it when you need it). That helps you coordinate with the browser's internal display cycles.
Something along these lines:
scheduleNext();
function scheduleNext() {
if (interval > 300) {
setTimeout(function() {
requestAnimationFrame(function() {
print();
scheduleNext();
});
}, interval);
}
}
Live Example:
var canvas = document.getElementById("canvas")
var context = canvas.getContext("2d")
var posX = 20;
var posY = 20;
var interval = 500;
function print() {
//background
context.fillStyle = "black";
context.fillRect(0, 0, 500, 500);
//object
context.fillStyle = "#4286f4";
context.fillRect(posX, posY, 50, 50);
posX = posX + 1;
posY = posY + 1;
}
scheduleNext();
function scheduleNext() {
if (interval > 300) {
setTimeout(function() {
requestAnimationFrame(function() {
print();
scheduleNext();
});
}, interval);
}
}
<canvas id="canvas" width="500" height="500">
setTimeout doesn't pause the script, it schedules the callback to be run in the given milliseconds. This means that you're scheduling all the callbacks right away, and there's only a 1 millisecond difference between them, which isn't enough to cause an observable difference.
Update :
ahaa , Yes its possible to create the animation loop with just "setTimeout" ,
function test () {
print ();
interval = interval - 10;
setTimeout(test, interval);
}
test ();

Capture photos from video after specific time in p5.js

var video;
var snapshots = [];
var readyCheck = false;
var button;
function setup() {
createCanvas(800, 600);
background(0);
video = createCapture(VIDEO, ready);
video.size(200, 150);
}
function ready() {
readyCheck = true;
console.log('work');
}
function draw() {
var w = 200;
var h = 150;
var x = 0;
var y = 0;
if (readyCheck) {
for (var i = 0; i < 100; i++) {
// use setTimeout() to wait for 2 seconds
setTimeout(function() {
snapshots[i] = video.get();
image(snapshots[i],x, y);
x += w;
if (x >= width) {
x = 0;
y += h;
}
}, 2000);
}
}
}
my purpose is taking pictures from the webcam after specific time. So I use the setTimeout() in JS. I expect pictures will appear on the canvas every 2 seconds in a row.
when entering the for part, the code will wait 2 seconds and capture the image from webcam and display it.
but my situation is that all the picture appear on the canvas at the same time.
You need to take a step back and understand how the draw() function and the setTimeout() functions work.
The draw() function is automatically called 60 times per second. You can adjust this by calling the frameRate() function or the noLoop() function. More info is available in the reference.
The setTimeout() function sets up a callback function that is automatically called after some duration, in your case 2 seconds.
So, what your code is doing is setting up 100 callback functions that will all fire in 2 seconds- and it's doing this 60 times per second! So in 1 second, you'll have 6000 functions that will start firing 2 seconds later! This is almost definitely not what you want.
P5.js already has its own timing mechanism in the draw() function that's called 60 times per second, so it seems a little weird to use the setTimeout() function inside P5.js code. Instead, you should probably set up your own timing using the frameCount variable or the millis() function.
Here's an example that shows a random color every second:
function setup() {
createCanvas(200, 200);
}
function draw() {
if(frameCount % 60 == 0){
background(random(256), random(256), random(256));
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.11/p5.min.js"></script>
This code uses the frameCount variable and the % modulus operator to check whether 60 frames have passed, and if so, it sets the background to a random color. You'll want to do something similar.
Like I said above, more info about all of this can be found in the reference.

javascript setTimeout and issues with a bunch of circles

I have a small program that I am supposed to write that makes a bouncy ball in a canvas. I can get a wireframe of a ball bouncing, but can't seem to get the setTimeout to fire at all. I have read, read and read about the function, but can't figure this out (new).
<!DOCTYPE HTML>
<html>
<head>
<title>basic Canvas</title>
<style>
#canvas1{
border:1px solid #9C9898;
}
body{
margin:0px;
padding:0px;
}
</style>
<script>
function drawMe(){
//Set x,y,radius
var x = 60;
var y = 60;
var radius = 70;
drawLoop(x,y,radius);
}
function drawLoop(x,y,radius){
var canvas2=document.getElementById("canvas1");
var ctx=canvas2.getContext("2d");
for(i=1;i<100;i++){
if(y + radius >= canvas2.height){
d = 1;
}
if(y - radius <= 0){
d = 0;
}
if (d==0){
x = x + 10;
y = y + 10;
}
else if (d==1){
x = x + 10;
y = y - 10;
}
draw(x,y,radius);
window.setTimeout(function() {draw(x,y,radius)},3000);
}
}
function draw(x,y,radius){
var canvas2=document.getElementById("canvas1");
var ctx=canvas2.getContext("2d");
ctx.beginPath();
ctx.arc(x,y,radius,0,2*Math.PI,false);
var gradient = ctx.createRadialGradient(x, y, 1, x, y, radius);
gradient.addColorStop(0,"blue");
gradient.addColorStop(1,"white");
ctx.fillStyle=gradient;
ctx.lineWidth=1;
ctx.strokeStyle="blue";
ctx.fill();
ctx.stroke();
}
</script>
</head>
<body onload="drawMe()">
<canvas id="canvas1" width=1000" height="400">
</canvas>
</body>
</html>
A little function called 'drawMe()' which sets x, y, and radius, then calls a little drawing loop that fires 100 times that draws the bouncy ball ('drawLoop'). at the bottom of the function drawLoop, I call draw, which actually drawls the circles. From what I've read, the line 'setTimeout(function(){draw(x,y,radius)};,3000); should call the draw function every three seconds. But it doesn't. What the heck am I doing wrong?
setTimeouts are counted from the time they are created. The loop runs almost instantly and creates the setTimeouts at almost the same time. They are then all ran 3 seconds later.
One way to get around this is in the solution below. This does not increment the loop until the current timeout has been completed.
http://jsfiddle.net/x8PWg/14/
This is only one of the many potential solutions to this.

html 5 canvas javascript rain animation(how to implement efficiently and easily!)

I tried both of these in canvas and nothing showed, also I doubt it is even efficient :/. I am trying to make rain that comes down the screen.. Wondering what is the most efficient way of doing this. I am a beginner at animation and would really appreciate help.
I suspect that creating a rain object would be best, each with the quality of coming down the screen then coming to the top and then an array with them...maybe with random x values withing the canvas width and y values of 0 but I don't know how to implement that. Please help!
xofRain = 20;
startY = 0;
ctx.beginPath();
ctx.moveTo(xofRain, startY);
ctx.lineTo(xofRain, startY + 20);
ctx.closePath();
ctx.fillStyle = "black";
ctx.fill();
function rain(xofRain){
startY = canvas.height();
ctx.moveTo(xofRain, startY);
ctx.beginPath();
ctx.lineTo(xofRain, startY + 3);
ctx.closePath();
ctx.fillStyle = "blue";
ctx.fill();
}
Here comes your answer, this snow rain is created using pure HTML5 Canvas, the technique used to achieve this animation is called "Double Buffer Animation". First it is good to know what is Double Buffer animation technique.
Double Buffer Technique: This is an advanced technique to make animation clear and with less flickers in it. In this technique 2 Canvas is used, one is displayed on webpage to show the result and second one is used to create animation screens in backed process.
How this will help full, suppose we have to create a animation with very high number of move, as in our Snow Fall example, there are number of Flakes are moving with there own speed, so keep them moving, we have to change position of each flake and update it on the canvas, this is quite heavy process to deal with.
So Now instead of updating each Flake directly on our page canvas, we will create a buffer Canvas, where all these changes take place and we just capture a Picture from Buffer canvas after 30ms and display it on our real canvas.
This way our animation will be clear and without flickers. So here is a live example of it.
http://aspspider.info/erishaan8/html5rain/
Here is the code of it:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>HTML5 Rain</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
article, aside, figure, footer, header, hgroup,
menu, nav, section { display: block; }
</style>
<script type="text/javascript">
var canvas = null;
var context = null;
var bufferCanvas = null;
var bufferCanvasCtx = null;
var flakeArray = [];
var flakeTimer = null;
var maxFlakes = 200; // Here you may set max flackes to be created
function init() {
//Canvas on Page
canvas = document.getElementById('canvasRain');
context = canvas.getContext("2d");
//Buffer Canvas
bufferCanvas = document.createElement("canvas");
bufferCanvasCtx = bufferCanvas.getContext("2d");
bufferCanvasCtx.canvas.width = context.canvas.width;
bufferCanvasCtx.canvas.height = context.canvas.height;
flakeTimer = setInterval(addFlake, 200);
Draw();
setInterval(animate, 30);
}
function animate() {
Update();
Draw();
}
function addFlake() {
flakeArray[flakeArray.length] = new Flake();
if (flakeArray.length == maxFlakes)
clearInterval(flakeTimer);
}
function blank() {
bufferCanvasCtx.fillStyle = "rgba(0,0,0,0.8)";
bufferCanvasCtx.fillRect(0, 0, bufferCanvasCtx.canvas.width, bufferCanvasCtx.canvas.height);
}
function Update() {
for (var i = 0; i < flakeArray.length; i++) {
if (flakeArray[i].y < context.canvas.height) {
flakeArray[i].y += flakeArray[i].speed;
if (flakeArray[i].y > context.canvas.height)
flakeArray[i].y = -5;
flakeArray[i].x += flakeArray[i].drift;
if (flakeArray[i].x > context.canvas.width)
flakeArray[i].x = 0;
}
}
}
function Flake() {
this.x = Math.round(Math.random() * context.canvas.width);
this.y = -10;
this.drift = Math.random();
this.speed = Math.round(Math.random() * 5) + 1;
this.width = (Math.random() * 3) + 2;
this.height = this.width;
}
function Draw() {
context.save();
blank();
for (var i = 0; i < flakeArray.length; i++) {
bufferCanvasCtx.fillStyle = "white";
bufferCanvasCtx.fillRect(flakeArray[i].x, flakeArray[i].y, flakeArray[i].width, flakeArray[i].height);
}
context.drawImage(bufferCanvas, 0, 0, bufferCanvas.width, bufferCanvas.height);
context.restore();
}
</script>
</head>
<body onload="init()">
<canvas id="canvasRain" width="800px" height="800px">Canvas Not Supported</canvas>
</body>
</html>
Also if you find this help full, accept as Answer and make it up. o_O
Cheers!!!
I'm not sure what "most efficient" is. If it was me I'd do it in WebGL but whether or not that's efficient is not clear to me.
In either case I'd try to use a stateless formula. Creating and updating state for every raindrop is arguably slow.
const ctx = document.querySelector("canvas").getContext("2d");
const numRain = 200;
function render(time) {
time *= 0.001; // convert to seconds
resizeCanvasToDisplaySize(ctx.canvas);
const width = ctx.canvas.width;
const height = ctx.canvas.height;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, width, height);
resetPseudoRandom();
const speed = time * 500;
ctx.fillStyle = "#68F";
for (let i = 0; i < numRain; ++i) {
const x = pseudoRandomInt(width);
const y = (pseudoRandomInt(height) + speed) % height;
ctx.fillRect(x, y, 3, 8);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
let randomSeed_ = 0;
const RANDOM_RANGE_ = Math.pow(2, 32);
function pseudoRandom() {
return (randomSeed_ =
(134775813 * randomSeed_ + 1) %
RANDOM_RANGE_) / RANDOM_RANGE_;
};
function resetPseudoRandom() {
randomSeed_ = 0;
};
function pseudoRandomInt(n) {
return pseudoRandom() * n | 0;
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
}
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>
Note that I could have used ctx.moveTo(x, y); ctx.lineTo(x, y + 8); for each line and then at the end of the loop called ctx.stroke(). I didn't do that because I'm assuming it would be less efficient than using ctx.fillRect. In order for the canvas to draw lines it actually has to allocate a dynamic path (you call ctx.beginPath). It then has to record all the lines you add. Then it has to expand those lines into vertices of various kinds to rasterize the lines. You can basically see the various algorithms it uses here. Conversely none of that has to happen with ctx.fillRect. No allocations have to happen (not saying they don't happen, just saying they don't have to). The canvas can just use a single pre-allocated quad and draw it on the GPU by passing the correct matrix to draw whatever rectangle you ask of it. Of course they're might be more overhead calling ctx.fillRect 200 times rather than ctx.moveTo, ctx.lineTo 200s + ctx.stroke once but really that's up to the browser.
The rain above may or may not be a good enough rain effect. That wasn't my point in posting really. The point is efficiency. Pretty much all games that have some kind of rain effect do some kind of stateless formula for their rain. A different formula would generate different or less repetitive rain. The point is it being stateless.

Categories