[HN Gopher] Making the whole web better, one canvas at a time
       ___________________________________________________________________
        
       Making the whole web better, one canvas at a time
        
       Author : pps
       Score  : 82 points
       Date   : 2021-10-10 19:58 UTC (2 days ago)
        
 (HTM) web link (bkardell.com)
 (TXT) w3m dump (bkardell.com)
        
       | koch wrote:
       | Nice to see the webkit demo, though unfortunately offscreencanvas
       | is not yet supported on mobile or desktop safari
       | 
       | https://caniuse.com/offscreencanvas
        
         | gfodor wrote:
         | gamdevs have been waiting for this for something like a decade.
         | I'd like to know if anything has changed to expect it to land
         | anytime soon esp in Safari.
        
         | jakelazaroff wrote:
         | Behind a flag in Firefox, too (and only supports WebGL). Very
         | eager to see this implemented more widely.
        
       | 3np wrote:
       | > What's interesting though, is that in reality, <canvas>'s
       | prevalence in the the HTTPArchive isn't so far behind <video>
       | (63rd/70th most popular elements respectively). It's considerably
       | more widely used than many other standard HTML elements.
       | 
       | > 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.
        
         | [deleted]
        
         | postalrat wrote:
         | You are almost certainly wrong. I've seen canvas used on many
         | pages for rendering documents. Some frameworks render a
         | majority of the UI in a canvas. Games are often in a canvas.
         | 
         | Fingerprinting and tracking done using many features. Canvas is
         | one but not the only. Blocking canvas probably makes it easier
         | to fingerprint you.
        
         | wgj wrote:
         | I didn't know about this, or have any idea how it might work,
         | so I looked it up.
         | 
         | https://en.wikipedia.org/wiki/Canvas_fingerprinting
        
       | dorianmariefr wrote:
       | The linked video demo is really impressive
       | https://youtu.be/8aAdP9nQSss?t=303 (rendering a mandelbrot from
       | barely usable to fully responsive and animated)
        
       | tomxor wrote:
       | Note that OffscreenCanvas is currently only implemented in
       | chromium based browsers [0]
       | 
       | 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...
        
         | devwastaken wrote:
         | Without off screen canvas and threading your UI and logic are
         | all being done in a single thread, causing significant lag
         | problems that every other kind of application has resolved
         | since the 90's. Threading is am essential part of any user
         | facing application, and browsers outside of chrome have
         | continually squandered the ability to put out good quality UI
         | that does intensive background work. Something as simple as a
         | video player in wasm requires at least 1 other thread.
        
           | jbluepolarbear wrote:
           | The core map code is slow, why doesn't the map code optimize
           | its runtime by using threads? Then the main thread canvas
           | just renders the current state? That's how you solve the
           | problem, not by creating an entire new canvas to solve the
           | crappy map code. You're right, people have solved these
           | problems with threads, but on a much better way. Google needs
           | to stop reinventing the wheel and fix their problems.
        
           | Steltek wrote:
           | There's still a major disconnect in this blog post. The
           | problem statement was making all web-enabled devices
           | performant with Canvas, not just desktops and mobiles running
           | Chrome. If Off Screen Canvas is only available for a recent
           | version of one browser engine, it's not going to solve that
           | problem is it?
        
         | neves wrote:
         | Sure,but don't underestimate the better user interactivity. The
         | ability to interact during the rendering is a HUGE usability
         | improvment.
        
           | lifthrasiir wrote:
           | The GP meant that a fundamentally slow code can't be made
           | faster _that_ much. Offloading canvas calls without making
           | the rendering itself faster can easily mask the performance
           | issue, which is very relevant (and frequently ignored) in
           | mobiles.
        
         | news_to_me wrote:
         | There are definitely legitimate use cases, like rendering text
         | tiles in parallel workers and letting the main thread blit them
         | all together. Text rendering is one of the slowest canvas
         | drawing operations, and blitting is super fast, so this can
         | make a huge difference in responsiveness if you're zooming or
         | scrolling.
        
       | pickledcods wrote:
       | For a high performance/responsive 2D canvas the best approach is
       | with webworkers and ArrayBuffer, like I did for this project:
       | 
       | https://rockingship.github.io/jsFractalZoom/jsFractalZoom.ht...
       | 
       | It can easily handle 4K resolution.
        
         | pickledcods wrote:
         | The biggest performance hit that is educated everywhere is
         | accessing pixels through getImageData() as bytes. In many cases
         | it is way better to access it as a 32-bits RGBA value, like
         | with:                 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);       }
        
       | z3t4 wrote:
       | I wish the article would have some code examples.
        
         | paulirish wrote:
         | Here's some:
         | https://developers.google.com/web/updates/2018/08/offscreen-...
         | https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/
         | https://medium.com/samsung-internet-dev/offscreencanvas-work...
        
       | smusamashah wrote:
       | OffscreenCanvas isn't supported by Firefox yet. Also, if a canvas
       | is just not attached to display it can act like offscreen canvas.
       | Drawing on it will be much faster than on screen canvas. When you
       | are done drawing, take the image data from offscreen canvas and
       | draw on the live canvas.
        
         | rikroots wrote:
         | This.
         | 
         | 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
        
       | btbuildem wrote:
       | Back in the day we used to call that "double buffering".
       | 
       | 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.
        
         | glass_of_water wrote:
         | From my understanding, OffscreenCanvas and double buffering are
         | unrelated. OffscreenCanvas just lets you execute draw calls in
         | a separate thread (in a WebWorker). I believe the browser
         | already uses double buffering under the hood for drawing to a
         | canvas element.
        
       | pavlov wrote:
       | Canvas is one of the only web APIs that's simple enough to
       | understand in one reading, yet flexible enough to be a real
       | application primitive -- and that's because it wasn't originally
       | designed for the web, but was simply a lightweight wrapper around
       | CoreGraphics for Apple's Dashboard in Mac OS X.
       | 
       | 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.
        
         | lifthrasiir wrote:
         | ...at the huge expense of accessibility though.
        
           | Matheus28 wrote:
           | This. Please don't use canvas for anything that doesn't
           | require it (games, visualizations, etc.). It's awful from an
           | accessibility perspective.
        
       | Graffur wrote:
       | It's nearly _good_. Still a bit janky imo
        
       | alphabet9000 wrote:
       | I have been disappointed by cross browser discrepancies in the
       | canvas element. Chrome and FF handle Alpha values very
       | differently, and the results are extremely noticeable if you do
       | anything that involves layering many semi-transparent layers on
       | top of each other. There are other floating point rounding error
       | quirks as well, e.g., firefox can translate a context by 0.001px
       | whereas chrome is limited to a precision of only 0.01px. These
       | discrepancies make it impossible to do certain things that look
       | the same on all browsers.
        
       | mhoad wrote:
       | Good time to be a Flutter developer. I'm really excited to see
       | where they can get on web in particular in the next couple of
       | years but this will certainly help.
        
       | beaconstudios wrote:
       | nice, sounds like double-buffering [1] for canvas is finally a
       | viable option.
       | 
       | [1]
       | https://www.khronos.org/opengl/wiki/Default_Framebuffer#Doub...
        
         | glass_of_water wrote:
         | Canvas already uses double buffering. OffscreenCanvas lets you
         | issue draw commands from a separate thread.
        
         | [deleted]
        
       | felixfbecker wrote:
       | Seeing canvas used always makes me sad. Why can't maps use SVG?
       | If there are blockers for that, I'd rather see the web resolve
       | those. canvas is a black box for screen readers, crawlers, the
       | semantic web. You end up reimplementing a DOM anyway for
       | accessibility, or not, if you don't care about accessibility,
       | which sucks.
        
         | no_circuit wrote:
         | SVG is XML which can have security issues due to parsing. And
         | to top it off SVG has a script tag itself. Basically a XSS
         | vector.
        
           | kaba0 wrote:
           | HTML also has a script tag, hell js has an eval! Should we
           | stop using those as well?
        
         | zdragnar wrote:
         | I've been part of an implementation of a global map done in
         | SVG. Only path data that was sent down was national borders. We
         | had to do many multiple levels because the volume of data to
         | get the paths drawn accurately was both too big and too slow
         | until you zoomed in a bit and could start loading individual
         | countries, then individual regions.
         | 
         | 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.
        
         | mhoad wrote:
         | I guess it would be fair to ask if canvas can eventually become
         | something to meet those challenges. Not by itself obviously but
         | when you consider things like Flutter built on top of it. It's
         | not there yet obviously but I could totally see a path in the
         | future where it handles all those use cases just fine and do so
         | with better performance.
        
       | akersten wrote:
       | I don't understand. Was it not possible to decouple the UI/events
       | thread from whatever performance intensive things your app might
       | be doing? We've had HTML5 games for a long time now and they've
       | figured this out.
       | 
       | Is this just a batteries-included way of forcing the thread
       | separation between logic and drawing?
        
         | bob1029 wrote:
         | There is a "best practices" way to do canvas drawing which
         | involves requestAnimationFrame inside a draw loop function, but
         | there is no way to put UI events and drawing on different
         | logical threads. The best you can do is make sure your draw
         | function does not do non-drawing things.
        
         | modeless wrote:
         | In theory it's possible to break up long running operations
         | into small chunks and schedule them between frames. In practice
         | it's a lot of work and the only people who bother are game
         | developers. Most app developers just don't care at all if they
         | have a 100ms pause. If you point it out to them they'll just
         | tell you that 100ms is nothing and nobody will notice, they
         | have better things to do than fix 100ms pauses. And so all apps
         | are janky.
         | 
         | > 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.
        
           | rictic wrote:
           | Expanding on this, if you do all the work on the main thread,
           | you're implementing a cooperative multitasking system, with
           | all of the problems that entails.
           | 
           | 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.
        
       ___________________________________________________________________
       (page generated 2021-10-12 23:02 UTC)