Hacker News new | comments | show | ask | jobs | submit login
Missing drawing API: Canvas vs SVG paradox (bajcic.com)
57 points by kodisha on Sept 21, 2013 | hide | past | web | favorite | 41 comments

If you want to draw a lot of data, you have to use canvas. If you want to have vector graphics, you have to use SVG. What are you supposed to do when you need both? Good luck to you my friend

We solved this problem for our domain by using SVG but bypassing the SVG DOM. Instead of making API calls to build up vector elements, we string together a gigantic blob of SVG markup as text and then splat it into the DOM in one go using innerHTML. This works surprisingly well—it's amazing how much computation you can cram in there without noticing any lag. We rebuild pretty much the entire UI on every mouse move and scroll.

At the time we came up with this trick, there was one big drawback: you couldn't put SVG elements inside HTML elements unless your doctype was XHTML. I believe that has changed with HTML5, but haven't ported that code yet. Can anyone add info here?

I have no idea how close this comes to Web/OpenGL; probably not very close, but it was a way out of the dilemma for us.

Edit: It used to puzzle me why this was so fast, but it seems obvious now. JS engines have optimized string concatentation, and the rendering of all that markup is happening on the C++ side, so both things that have to happen here are relatively quick. What's slow is the trip across the Grand Canyon from JS to the rendering engine, and this technique only does that once. Essentially, you're batching up all your API calls, and that saves you so much that the fact that you're using a horrifically inefficient text protocol to do it doesn't matter.

You can use document fragments for this and get a similar performance boost without resorting to string concatenation.

Do you mean SVG document fragments? Can you explain in more detail or point to an example?

Here is an introduction. They are not SVG specific but you can build SVG elements in a document fragment, which is a lot faster than appending to the DOM, then append the fragment to the DOM all in one go.

See http://ejohn.org/blog/dom-documentfragments/ or google for an introduction.

Building strings and using innerHTML is a very ugly way to get performance, you should definitely try to using the document fragment approach as it has similar performance characteristics.

So these are regular document fragments? I vaguely remember that we tried that and it didn't work nearly as well, but it's been a while.

Building strings and using innerHTML is a very ugly

Ah. Then you'll be dismayed to hear we use tables for layout as well :)

In fact, using a document fragment is actually faster at least in some cases: http://jsperf.com/svg-fragment-test

The naiive approach was fastest for this example, but that's probably because the document was so small.

> Could anyone add info here.

It works - desktop and mobile.

I have recently successfully used inline SVG in IE10, Firefox, Chrome and Safari. I had issues, and found bugs, but I also found workarounds fairly quickly.

I used the html5 doctype and the default Content-Type:

Oh good. I had nightmares of not being able to still do this.

Care to describe the bugs and workarounds?

We wanted the SVG to be responsive, but altering the size of the svg itself didn't work well in all browsers. So we put it in a div and resized that and told the svg to fill the div. We have had to set the height on the div otherwise it defaults to the native height of the svg in some browsers, rather than the resized height. The svg did maintain it's proportions however.

Also, we wanted to dome some simple onclick and onhover events on certain elements within the svg, and to do this we had to keep the <script></script> stuff outside the SVG because of safari. Interestingly having the javascript within the SVG worked well with the other browsers...

Thanks! I came back to add a belated comment and am very glad to see your answer.

My belated comment is that I just upgraded that code to HTML5 and it works fine.

You should look into React. It has a similar approach (rerender everything) but handles the DOM construction and modification in an intelligent and performant way.

There's even an (underdocumented so far) React library for drawing to either Canvas, SVG, or VML (using the same API):


Do you have a live exemple somewhere ? i'm curious about how well this technique performs.

Not at present, sorry.

Canvas is a vector drawing API. Scaling a drawing to retina resolution or 300 DPI for printing is trivial.

Ultimately if you want to draw truly giant giant data sets, you need WebGL. What's missing isn't a new browser drawing API, but a good WebGL charting library.

Two.js[1] has a WebGL renderer and abstractions for basic vector graphics and polygons. I haven't had the chance to use it in a project yet but it seems promising. Two.js could even be used as a basis for a WebGL charting library (thus providing another layer of abstraction).

[1] http://jonobr1.github.io/two.js/

Thanks, that sounds really promising, will give it a try!

Canvas is ok, i also first tought on GL to solve that sort of problem..

But speaking of it, canvas has a sort of a bad api design and its not very optimized for GPU, you know..





Since Adobe is now on the HTML side of things, I wonder if they could propose a good api, or at least try to make a optimized api version of the canvas.. as this is definetly their domain..

Also i remember to see a proposal for a more mature path api from that guy behind Gallium, Years ago .. Zac Riusin(or something) that looked promissing..

I second that. you can model all your vectors pretty trivially just using existing canvas api.

I don't understand why the author didn't just do his canvas drawing in a resolution independent (or quasi-independent) way. Just choose a unit relative to the size of the canvas, and do everything in those units, transforming your coordinates to fit the width of the canvas element, which is resized to fit the device.

Sounds like you need a library like ART, which is described as a "Retained mode vector drawing API designed for multiple output modes".


Essentially, you write your drawing code once and it can render to Canvas, SVG, or VML depending on what you need. It doesn't yet have great documentation or a ton of examples, but it's in use at Facebook in their ads management and the page insights interface.

There's even bindings to Facebook's React (http://facebook.github.io/react/) drawing library which is a declarative library for building efficient, reusable view components: https://github.com/facebook/react-art. I haven't had a chance to play around too much yet but it looks very promising.

Retina canvas isn't that hard. Set the width and height attributes of your canvas to be their CSS dimensions * devicePixelRatio, and scale all your drawing calls by devicePixelRatio.

I agree that it's annoying that you have to think about it, but since it's a low-level drawing API, I understand why it's done that way.

One thing to note with respect to SVG and Canvas is that these APIs are not nearly as optimized in browsers as HTML is.

Since the web is predominantly HTML, Chrome and other browser devs have focused all their efforts on optimizing it (and JS).

SVG and Canvas rendering has not gotten that kind of attention. However it sounds like they are working on it now.

The upshot is that even if it doesn't seem like it would work today doesn't mean it's dead in the water and we need yet another drawing API. Give it time.

It also means -- and I know this sounds crazy -- that you could try drawing your dots with absolutely positioned 1px by 1px DIVs. I know this sounds absurd but I once worked on a scrolling + scaling timeline widget that performed 10x better when implemented with absolutely positioned HTML DIVs than in SVG.

(And like gruesom noted, it is probably best to blast all your DIVs the DOM in one shot.)

I can second this. Did a mind map + force directed graph where the lines connected at the edges of each node and each node had to be markup/css (so canvas was out). Making the dots slide around the node perimeter smoothly ended up working best by doing some trig and placing them absolutely within the node div. Super hacky but it was what worked.

I've done this too (on http://ssz.fr/ker/ for example) and I've noticed if indeed, HTML DOM was slightly faster than SVG up to a couple Firefox versions ago, I have found it to be consistently slower on Chromium, and about the same in the latest Firefox versions.

And 40k elements is a no way with any method other than canvas on Firefox, though it can sometimes stay somewhat usable with SVG on Chromium.

Interesting. I should point out my experience with this is from about 18 months ago (Spring 2012).

Wow, thats bold. I would love to do some experiments with divs, but i would really hesitate to put something like that in production.

I think that comments like this confirm that we really need new/better APIs, and i hope that browsers will get into it.

I am a bioinformatician interested in visualizations too. I totally understand your situation wanting to use D3 but can't because of the amount of bio data.

I've been trying to develop a toy genome browser as a pet project while procrastinating on writing my thesis. I faced a similar problem when rendering a genome feature density plot and found mouse interactions with it was painfully slow. I solved it by rendering the svg with d3 and then use canvg.js to convert the svg into canvas. You can see the toy example here: http://www.nextgenetics.net/tools/browser/browser.html

This prototype shows all genes from human chromosome 1. You can use WASD to scroll and Q/E to zoom in and out. The white density plot on the bottom is d3 made and converted into canvas with canvg. All svg are rendered with d3 using an element pooling system.

While I don't have a solution, I'm facing very similar challenges.

Ah, good to know there area others out there :)

I really liked the object model of paper.js, but the very same layers/object model led to some performance issues ol large data sets. So im stuck with custom canvas implementation with no vector/high res export solution.

Access to WebGL really helps.

Have a look at this fiddle:


25,000 3D cubes - with the required 8 vertices and 6 faces - each gently rotating around its X-axis in real time. Use your pointing device to pan, zoom and rotate the display.

Built with the Three.js library. Requires fast machine with good GPU and browser that supports WebGL.

This reminds me of http://famo.us/

What do they do? (I don't want to sign up to find out.)

Nothing it is just vaporware. they put a demo out with some music there promising the next generation html5 framework , but it was only to get funding. This is pure Scamware.

What is funny is the author of Three.js did exactly the same demo ( without the cheesy music ) with his framework, the difference is he has an real framework used by thousand of developpers and he did not need 2 million dollar funding to create it.

Take the output of d3 before it is drawn as svg and use it to draw to the canvas instead.

24.00? I would argue for a sample of the data and not the complete dataset.

See linked screenshot.

Also there is one funcionality you want there: type in gene name (or prefix) and highlight matches and fade out rest of them to lets say 0.05 alpha.

I think that's a typo for 24.000, which is how UK people render "Twenty-four thousand"

No, it's how some European countries write 24,000. The UK (like the US) uses . as the radix point and , for digit grouping.

Oh, totally my bad, it was a typo. its 24k.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact