Haven't worked with canvas in a while and I haven't looked at your code. But I remember from several canvas frameworks that if you want to animate multiple things it is much more performant to do it in multiple canvas elements.
Programming
Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!
Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.
Hope you enjoy the instance!
Rules
Rules
- Follow the programming.dev instance rules
- Keep content related to programming in some way
- If you're posting long videos try to add in some form of tldr for those who don't want to watch videos
Wormhole
Follow the wormhole through a path of communities [email protected]
script.js, ll. 57ff.
fillRect() {
var sideColor = this.on_side_one ? this.c_side_1 : this.c_side_2;
ctx.fillStyle = sideColor;
ctx.fillRect(g_pixelOffset + (g_pixelOffset * this.x), g_pixelOffset + (g_pixelOffset * this.y), this.radius, this.radius);
}
All these computations don’t have to be delayed until you’re actually drawing the dot. If you’d compute them when you’re creating the dot, the rendering might become smoother.
oh jeez! yeah that makes sense will add them to constructor thank you!
Use requestAnimationFrame for the drawing loop. Dont instantiate new objects unless needed
You’re mixing spaces and tabs for indentation. This makes it hard to read for anyone whose tab settings aren’t the same as yours.
Ah good to know just using tabs in vim but in two different machines so might be the issue— thanks!
First: For every performance optimization you do, you must actually measure if it improves things. It could be that you’re doing a perfectly sensible optimization, which turns out to work against the compiler (yes, even JavaScript is compiled) and thus decreases performance.
In script.js, l.149ff:
drawImage(Image, topLeftX, topLeftY) {
for (let row = 0; row < Image.length; row++) {
for(let column = 0; column < Image[row].length; column++) {
this.nextFrame[(topLeftX + column)*this.height + (topLeftY+row)] = Image[row][column];
}
}
}
The term topLeftX + column * this.height
can be rewritten as topLeftX * this.height + column * this.height
. The first term of this is a constant and can be extracted into a variable const verticalOffset = topLeftX * this.height
.
Similarly, topLeftY + row
doesn’t need to be recomputed in every iteration of the inner for loop. Move it out into the outer for loop.
Also notice that the variable part, column * this.height
is all the integer multiples of this.height
. Therefore, instead of a multiplication, you can simply add this.height
to a running total. Sums should be faster than multiplications.
With these three changes you get [Edit: I think I made mistakes during the replacements.]
drawImage(Image, topLeftX, topLeftY) {
const TODO_NAME_THIS = topLeftX * this.height
let TODO_NAME_THIS_ALSO = topLeftY
for (let row = 0; row < Image.length; row++) {
let TODO_NAME_THIS_TOO = 0
for(let column = 0; column < Image[row].length; column++) {
this.nextFrame[TODO_NAME_THIS + TODO_NAME_THIS_TOO + TODO_NAMES_THIS_ALSO] = Image[row][column];
TODO_NAME_THIS_TOO += this.height
}
TODO_NAME_THIS_ALSO += row
}
}
Ah yes I see! That should offer some improvement given the number of times it’s called— also see I’m used to swift so I keep using let when I should probably use const— thanks I’ll give it a try and let you know what happens!
With all these comments said, I experience no problems in Firefox on Linux, and I really like the art style. If one browser is troublesome to you, check which function(s) really are the culprit. (This is actually true for any optimization: Only optimize the stuff that’s slow (duh)).
script.js, ll. 788ff.
for (var i = 0; i < elements.length; i++)
If you’re not using the index of an iteration, you might as well use the array iterator. This is more of a legibility improvement rather than a performance improvement.
for (const element of elements)
A random suggestion would be to draw to multiple canvases, and use a CSS animation for the delay.
Also not sure if you are already doing this but it might be more peformant to use the raw buffer instead of draw functions.
Alternatively you could look into webgpu, it is ment for these kind of things.
I’ve thought about setting whole ‘images’ inside the canvas at once, but that would probably ruin the pixel-by-pixel style OP was going for. Do you have a suggestion how that could be maintained while not drawing every pixel individually?
All the calculations could be done before hand and stored and then the only thing left in the delayed draw is to set the buffer.
I haven't looked at the code yet so not sure how much if any it will save though.
Could also group pixels that are far away from eachother into a single call, while a compromise i think it will maintain the effect.