Making the whole web better, one canvas at a time
bkardell.com
bkardell.com
It is a neat idea, basically separating all the canvas calls into a webworker so it can run on it's own thread. However this scale of parallelism is not a solution to fundamentally slow code... the map rendering performance is still poor, it's just been prevented from interfering with the main thread. Please don't throw web workers at stuff before asking why the code or API calls are slow first.
[0] https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCa...
> Amazing, right? I mean, how could that even be?!
> The short answer is, it's just harder to recognize. A great example of this is maps. As a user, you recognize maps. You know they are common and popular. But what perhaps you don't recognize that it's on a canvas.
I’d posit that the majority of canvases in the wild is tracking and fingerprinting, not something that ends up visible to the user.
Unfortunately, privacy-conscious users are best off blocking canvas by default, like Tor Browser does.
Fingerprinting and tracking done using many features. Canvas is one but not the only. Blocking canvas probably makes it easier to fingerprint you.
Google's Recaptcha uses canvas for fingerprinting, for one.
Does disabling canvas disable recaptcha's fingerprinting? Or does it make it more effective?
Maybe Apple should have provided an NSView wrapper too, with a drawRect() method to draw into a canvas, and made DOM elements part of that view tree. That would have been a much simpler API for web components than what the committees came up with.
https://rockingship.github.io/jsFractalZoom/jsFractalZoom.ht...
It can easily handle 4K resolution.
const rgba = new Uint32Array(getImageData(0, 0, width, height).data.buffer);
rgba[y * width + x] = R | G << 8 | B << 16 | A << 24;
And using Array.subarray as substitute for memcpy() for block copy: function memcpy(dst, dstOffset, src, srcOffset, length) {
src = src.subarray(srcOffset, srcOffset + length);
dst.set(src, dstOffset);
}The one major stumbling block not mentioned in the article is accessibility. OffscreenCanvas offers nothing to solving that problem.
Also, I don't understand people who complain that 2d canvas is slow; it isn't. What slows things down is poor planning and execution of the animation. For instance - does the entire Mandlebrot Set need to be calculated on every RequestAnimationFrame iteration? If I was tackling the problem I'd only calculate the data when one of the significant parameters changed - in a worker farm, wasm, whatever - and only update the display canvas when the results emerged.
My canvas library[1] relies heavily in normal canvases not attached to the DOM. Not only does it speed things up massively, it also allows the library to do things like make the canvas responsive[2].
[1] - Scrawl-canvas - https://scrawl-v8.rikweb.org.uk/
[2] - CodePen demo of a responsive canvas - https://codepen.io/kaliedarik/pen/jOmWwWy
Rendering rasterized tiles from a tile server onto a canvas means that, on the client, performance is identical regardless of zoom level.
Basically, we went with SVG because only drawing the borders looked cool, and we could fake some clever zoom animations, and we couldn't afford to purchase custom tile data and run a tile server.
As for interactive bits, those are frequently done with DOM nodes positioned over the map. Maps make for a pretty crap user experience for screen readers, so if you really wanted to make the information accessible, you're better off putting it in a separate interface. That, and I can honestly say I have yet to see a map that would make sense to have a crawler attempt to index.
The common Amiga implementation was especially neat. There, the video buffer was (almost) any chunk of memory you pointed the "screen" at. You could pre-render a number of complex frames, and have a high-FPS animation by moving the buffer pointer.
And yes, you could look at your own code executing in realtime. But I digress.
Is this just a batteries-included way of forcing the thread separation between logic and drawing?
> Is this just a batteries-included way of forcing the thread separation between logic and drawing?
It's not possible to draw canvases on multiple threads without this. So it's about enabling, not just forcing. Enabling multiple threads is a big deal because every device has multiple cores these days and the web is mostly using only one.
It's surprisingly hard to break up potentially long tasks in an ergonomic way without losing a ton of performance, tracking down a long tail of tasks that block rendering, or both. Having multiple threads of execution is so much better.
[1] https://www.khronos.org/opengl/wiki/Default_Framebuffer#Doub...