539

What is the best approach for creating elements on the fly and being able to move them around? For example, let's say I want to create a rectangle, circle and polygon and then select those objects and move them around.

I understand that HTML5 provides three elements that can make this possible: svg, canvas and div. For what I want to do, which one of those elements will provide the best performance?

To compare these approaches, I was thinking of creating three visually identical web pages that each have a header, footer, widget and text content in them. The widget in the first page would be created entirely with the canvas element, the second entirely with the svg element, and the third with the plain div element, HTML and CSS.

Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
Agustinus Verdy
  • 7,267
  • 6
  • 26
  • 28
  • 1
    For those of you new to this thecnology this [video](http://vimeo.com/6691519) covers both SVG and Canvas and other details about how that integrates on html5. – Paulo Bueno Aug 03 '12 at 03:32
  • 17
    Short answer: Canvas is to MS Paint as SVG is to MS Powerpoint. Canvas is raster, SVG is vectorial. – GetFree Jan 16 '14 at 03:42
  • 9
    Dear reader: take all the comparisons and statements here with a grain of salt and do look at the date of the posts and comments. Times have changed and will change. Relative performance and even the options you have will change. E.g. most answers where written when there was no WebGL, which definitely is an alternative - it will be outdated in a few years, too, but as of today it may be very relevant. – Sebastian Aug 29 '18 at 08:25
  • @Sebastian which would you recommend today? if given a base size (e.g., 1280x800) and if you're willing to scale elements manually in code or use percentages all the time, is there an advantage of SVG to using DIVs? – Crashalot Jan 06 '19 at 04:10
  • 1
    @Crashalot - there is no single best solution. See my answer below and [here](https://stackoverflow.com/a/49709860/351836) for some more comments. If a few rectangles or maybe round rectangles is all you need, then `div`s are fine. I prefer SVG due to its simplicitly, explicity (coordinates and immunity against CSS changes) over divs. Complex text rendering is easier using HTML, of course. If it's just rectangles (but tens of thousands of it), WebGL is worth a look, but otherwise overkill. – Sebastian Jan 07 '19 at 07:44
  • @Sebastian thanks so much! the key concern is scaling designs from an online design editor (think simplified photoshop) so assume <= 100 elements and can use SVG for embedding complicated shapes. would you mind chiming in here? https://stackoverflow.com/questions/54064963/is-there-a-difference-between-scaling-elements-in-svg-and-html?noredirect=1#comment94968315_54064963 – Crashalot Jan 07 '19 at 08:26
  • @Sebastian also what do you mean by explicity? can't you achieve the same explicitness in HTML? – Crashalot Jan 07 '19 at 08:26
  • @Crashalot - by explicity I mean simple, honest "x" and "y" coordinates and transforms, rather than complicated div and flex absolute and relative layouts, with margins, insets, paddings, CSS hacks and differences, that look differently on every browser and change with every change in the CSS or font rendering engine etc. – Sebastian Jan 07 '19 at 13:37
  • @Sebastian thanks for the clarification! if you get a chance, would also love your thoughts on this SVG question: https://stackoverflow.com/questions/54064963/is-there-a-difference-between-scaling-elements-in-svg-and-html?noredirect=1#comment94968315_54064963 – Crashalot Jan 07 '19 at 17:41
  • @robertc thanks for sharing the link, but many images are missing on the page. do you still find the recommendation true, or could HTML/CSS(div, span, img) work as well as SVG for vector-like designs if you manually scale elements with JavaScript? – Crashalot Jan 07 '19 at 18:40

10 Answers10

640

The short answer:

SVG would be easier for you, since selection and moving it around is already built in. SVG objects are DOM objects, so they have "click" handlers, etc.

DIVs are okay but clunky and have awful performance loading at large numbers.

Canvas has the best performance hands-down, but you have to implement all concepts of managed state (object selection, etc) yourself, or use a library.


The long answer:

HTML5 Canvas is simply a drawing surface for a bit-map. You set up to draw (Say with a color and line thickness), draw that thing, and then the Canvas has no knowledge of that thing: It doesn't know where it is or what it is that you've just drawn, it's just pixels. If you want to draw rectangles and have them move around or be selectable then you have to code all of that from scratch, including the code to remember that you drew them.

SVG on the other hand must maintain references to each object that it renders. Every SVG/VML element you create is a real element in the DOM. By default this allows you to keep much better track of the elements you create and makes dealing with things like mouse events easier by default, but it slows down significantly when there are a large number of objects

Those SVG DOM references mean that some of the footwork of dealing with the things you draw is done for you. And SVG is faster when rendering really large objects, but slower when rendering many objects.

A game would probably be faster in Canvas. A huge map program would probably be faster in SVG. If you do want to use Canvas, I have some tutorials on getting movable objects up and running here.

Canvas would be better for faster things and heavy bitmap manipulation (like animation), but will take more code if you want lots of interactivity.

I've run a bunch of numbers on HTML DIV-made drawing versus Canvas-made drawing. I could make a huge post about the benefits of each, but I will give some of the relevant results of my tests to consider for your specific application:

I made Canvas and HTML DIV test pages, both had movable "nodes." Canvas nodes were objects I created and kept track of in Javascript. HTML nodes were movable Divs.

I added 100,000 nodes to each of my two tests. They performed quite differently:

The HTML test tab took forever to load (timed at slightly under 5 minutes, chrome asked to kill the page the first time). Chrome's task manager says that tab is taking up 168MB. It takes up 12-13% CPU time when I am looking at it, 0% when I am not looking.

The Canvas tab loaded in one second and takes up 30MB. It also takes up 13% of CPU time all of the time, regardless of whether or not one is looking at it. (2013 edit: They've mostly fixed that)

Dragging on the HTML page is smoother, which is expected by the design, since the current setup is to redraw EVERYTHING every 30 milliseconds in the Canvas test. There are plenty of optimizations to be had for Canvas for this. (canvas invalidation being the easiest, also clipping regions, selective redrawing, etc.. just depends on how much you feel like implementing)

There is no doubt you could get Canvas to be faster at object manipulation as the divs in that simple test, and of course far faster in the load time. Drawing/loading is faster in Canvas and has far more room for optimizations, too (ie, excluding things that are off-screen is very easy).

Conclusion:

  • SVG is probably better for applications and apps with few items (less than 1000? Depends really)
  • Canvas is better for thousands of objects and careful manipulation, but a lot more code (or a library) is needed to get it off the ground.
  • HTML Divs are clunky and do not scale, making a circle is only possible with rounded corners, making complex shapes is possible but involves hundreds of tiny tiny pixel-wide divs. Madness ensues.
Simon Sarris
  • 62,212
  • 13
  • 141
  • 171
  • 4
    The [Cake](http://code.google.com/p/cakejs/) library is another example of doing moveable objects and animations with objects on a canvas – SiggyF May 04 '11 at 20:59
  • Wrong :P div's can scale if the browser is using hw accelerated CSS engine, css art is different and besides Canvas and SVG are the proper choice here , CSS art / div art is just when u dont need to overkill just a small overlay :P – ShrekOverflow Dec 06 '12 at 18:40
  • Concerning DIVs, if you want to make circles/special shapes and is not going to change its image/sprite due course, you can just create a PNG and use it as `background-image`... Though you can do similar things in SVG/Canvas – luiges90 Jul 07 '13 at 05:21
  • 4
    What if you are creating an interactive map game? :p – Anthony Nov 20 '13 at 22:30
  • This was created using (non-nested) DIVs and CSS 3D transforms, so I'd say DIVs are not slow at all: https://www.youtube.com/watch?v=fzBC20B5dsk – Erik Kaplun Dec 06 '16 at 10:18
  • I'm comparing hundreds of thousands of moving objects, not a hundred or so. – Simon Sarris Dec 16 '16 at 20:16
  • Dear reader: take all the comparisons and statements here with a grain of salt and do look at the date of the posts and comments. Times have changed and will change. Relative performance and even the options you have will change. See my comment about WebGL below, e.g. - it will be outdated in a few years, too, but as of today it may be very relevant. – Sebastian May 14 '18 at 08:28
  • upvoted! what do you think about SVG vs canvas for a live candlestick chart, the thing I noticed so far is that you have lots of draws and the time axis shifts towards the left every N minutes – PirateApp Aug 24 '18 at 06:09
41

To add to this, I've been doing a diagram application, and initially started out with canvas. The diagram consists of many nodes, and they can get quite big. The user can drag elements in the diagram around.

What I found was that on my Mac, for very large images, SVG is superior. I have a MacBook Pro 2013 13" Retina, and it runs the fiddle below quite well. The image is 6000x6000 pixels, and has 1000 objects. A similar construction in canvas was impossible to animate for me when the user was dragging objects around in the diagram.

On modern displays you also have to account for different resolutions, and here SVG gives you all of this for free.

Fiddle: http://jsfiddle.net/knutsi/PUcr8/16/

Fullscreen: http://jsfiddle.net/knutsi/PUcr8/16/embedded/result/

var wiggle_factor = 0.0;
nodes = [];

// create svg:
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute('style', 'border: 1px solid black');
svg.setAttribute('width', '6000');
svg.setAttribute('height', '6000');

svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink",
    "http://www.w3.org/1999/xlink");

document.body.appendChild(svg);


function makeNode(wiggle) {
    var node = document.createElementNS("http://www.w3.org/2000/svg", "g");
    var node_x = (Math.random() * 6000);
    var node_y = (Math.random() * 6000);
    node.setAttribute("transform", "translate(" + node_x + ", " + node_y +")");

    // circle:
    var circ = document.createElementNS("http://www.w3.org/2000/svg", "circle");
    circ.setAttribute( "id","cir")
    circ.setAttribute( "cx", 0 + "px")
    circ.setAttribute( "cy", 0 + "px")
    circ.setAttribute( "r","100px");
    circ.setAttribute('fill', 'red');
    circ.setAttribute('pointer-events', 'inherit')

    // text:
    var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
    text.textContent = "This is a test! ÅÆØ";

    node.appendChild(circ);
    node.appendChild(text);

    node.x = node_x;
    node.y = node_y;

    if(wiggle)
        nodes.push(node)
    return node;
}

// populate with 1000 nodes:
for(var i = 0; i < 1000; i++) {
    var node = makeNode(true);
    svg.appendChild(node);
}

// make one mapped to mouse:
var bnode = makeNode(false);
svg.appendChild(bnode);

document.body.onmousemove=function(event){
    bnode.setAttribute("transform","translate(" +
        (event.clientX + window.pageXOffset) + ", " +
        (event.clientY + window.pageYOffset) +")");
};

setInterval(function() {
    wiggle_factor += 1/60;
    nodes.forEach(function(node) {

        node.setAttribute("transform", "translate(" 
                          + (Math.sin(wiggle_factor) * 200 + node.x) 
                          + ", " 
                          + (Math.sin(wiggle_factor) * 200 + node.y) 
                          + ")");        
    })
},1000/60);
Neuron
  • 5,141
  • 5
  • 38
  • 59
knut
  • 689
  • 5
  • 13
  • 2
    We settled on SVG too, after trying desperately to get Canvas to work for us. We have a very large diagram and SVG was by far the most efficient, plus the auto-scaling on retina screens is a massive bonus. – Fijjit Jan 30 '15 at 20:13
  • knut and @Fijjit did you consider using DIVs instead of SVG? if given a base size (e.g., 1280x800) couldn't you manually scale DIVs so they look as sharp as SVG? thanks for your help! – Crashalot Jan 06 '19 at 04:08
28

Knowing the differences between SVG and Canvas would be helpful in selecting the right one.

Canvas

SVG

  • Resolution independent
  • Support for event handlers
  • Best suited for applications with large rendering areas (Google Maps)
  • Slow rendering if complex (anything that uses the DOM a lot will be slow)
  • Not suited for game application
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Leo The Four
  • 699
  • 9
  • 14
  • 13
    why do people say Canvas is resolution dependent? i understand that once the bitmap has been rendered it doesn't scale nicely. but you can redraw on resolution size changes, so how isn't that resolution independent? – Alex Bollbach Aug 17 '17 at 20:34
  • 6
    @AlexBollbach - Canvas is resolution dependent, because you need to take into account (depend) on the resolution in order to get good results. With SVG you don't care about the resolution. Good luck with getting non-jaggy lines on a 2400DPI printer and a Canvas based rendering. No problem with SVG. – Sebastian Apr 23 '19 at 11:58
22

While there is still some truth to most of the answers above, I think they deserve an update:

Over the years the performance of SVG has improved a lot and now there is hardware-accelerated CSS transitions and animations for SVG that do not depend on JavaScript performance at all. Of course JavaScript performance has improved, too and with it the performance of Canvas, but not as much as SVG got improved. Also there is a "new kid" on the block that is available in almost all browsers today and that is WebGL. To use the same words that Simon used above: It beats both Canvas and SVG hands down. This doesn't mean it should be the go-to technology, though, since it's a beast to work with and it is only faster in very specific use-cases.

IMHO for most use-cases today, SVG gives the best performance/usability ratio. Visualizations need to be really complex (with respect to number of elements) and really simple at the same time (per element) so that Canvas and even more so WebGL really shine.

In this answer to a similar question I am providing more details, why I think that the combination of all three technologies sometimes is the best option you have.

Sebastian
  • 7,729
  • 2
  • 37
  • 69
  • 1
    Unix users should take note that hardware acceleration is disabled by default on both Firefox and Chromium, still true in mid 2019. – NVRM Jul 31 '19 at 20:20
  • @NVRM - this is about hardware-acceleration of CSS and SVG, not about video decoding. AFAIK the former has been available for years: [Check output of chrome://gpu](https://bugs.chromium.org/p/chromium/issues/detail?id=741063#c3) – Sebastian Aug 01 '19 at 07:18
  • `layers.acceleration.force-enabled` in Firefox is not about video decoding. It's a well known fact. When done loops using requestAnimationFrame is an other level, allowing way more repaints. Not about video at all. – NVRM Aug 01 '19 at 18:18
  • @NVRM - can you provide links to FF and Chromium bugs for these GPU issues on Linux, please? Also note that by "hardware accelerated" I was not only referring to GPU acceleration, but also multi-threaded compositing and animations, like e.g. loading spinners that keep spinning while no JavaScript is running or *while* JS is being executed. This is impossible with Canvas and relative to pure "JavaScript" it is indeed some kind of hardware acceleration (multi-threading) that is definitely available in Chrome and FF on all platforms. Thanks! – Sebastian Aug 02 '19 at 07:30
  • https://hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank/ «Browsers are still in the process of making this shift. Some browsers paint on the GPU all of the time, while others only do it on certain platforms (like only on Windows, or only on mobile devices).» – NVRM Aug 02 '19 at 15:33
  • 1
    To sum up the current situation: Works for me on Chrome and Chromium. On Linux. In 2019. On all instances I tested without special configuration. Firefox/Mozilla is [working on it for Linux](https://www.reddit.com/r/firefox/comments/bc4ci3/webrender_enabled_by_default_on_linux_nightly/), however out of process rendering is not something new for FF, either and will always work better with SVG, CSS, etc. than it can for Canvas. – Sebastian Aug 06 '19 at 09:45
  • @Sebastian You're the only answer here that mentions CSS with: `now there is hardware-accelerated CSS transitions and animations for SVG`. Does this mean transitions/animations are hardware accelerated only if written in CSS? I've been investigating javascript APIs for animating SVG and based on this answer now I'm wondering if I should be looking at CSS approaches instead (for better performance). – ecoe Nov 28 '20 at 02:57
  • @ecoe - it depends what other approaches you are looking at. As soon as JS is involved (per frame), definitely it could be faster without JS. If JS is only used for setting up the [animation](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate), it's probably OK. If the JS runs and calculates each frame, as is the case with Canvas and also mostly for WebGL, it can never be as efficient. – Sebastian Nov 30 '20 at 11:30
  • 1
    @Sebastian thanks and I read the [blog in your other answer](https://www.yworks.com/blog/svg-canvas-webgl), but it only showed SVG faster than Canvas in one "complex" test case and they stated: `where only simple... ...the performance of Canvas is actually better if you have lots and lots of elements on the screen at the same time.` This is the best investigation I've seen and the interactive demo is superb! Though even with my own testing I wasn't able to see much difference between svg/canvas for the complex cases, so I'm still wondering how true is it SVG can outperform canvas? – ecoe Dec 16 '20 at 01:21
19

I agree with Simon Sarris's conclusions:

I have compared some visualization in Protovis (SVG) to Processingjs (Canvas) which display > 2000 points and processingjs is much faster than protovis.

Handling events with SVG is of course much easer because you can attach them to the objects. In Canvas you have to do it manually (check mouse position, etc) but for simple interaction it shouldn't be hard.

There is also the dojo.gfx library of the dojo toolkit. It provides an abstraction layer and you can specify the renderer (SVG, Canvas, Silverlight). That might be also an viable choice although I don't know how much overhead the additional abstraction layer adds but it makes it easy to code interactions and animations and is renderer-agnostic.

Here are some interesting benchmarks:

Ümit
  • 17,379
  • 7
  • 55
  • 74
18

Just my 2 cents regarding the divs option.

Famous/Infamous and SamsaraJS (and possibly others) use absolutely positioned non-nested divs (with non-trivial HTML/CSS content), combined with matrix2d/matrix3d for positioning and 2D/3D transformations, and achieve a stable 60FPS on moderate mobile hardware, so I'd argue against divs being a slow option.

There are plenty of screen recordings on Youtube and elsewhere, of high-performance 2D/3D stuff running in the browser with everything being an DOM element which you can Inspect Element on, at 60FPS (mixed with WebGL for certain effects, but not for the main part of the rendering).

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
13

For your purposes, I recommend using SVG, since you get DOM events, like mouse handling, including drag and drop, included, you don't have to implement your own redraw, and you don't have to keep track of the state of your objects. Use Canvas when you have to do bitmap image manipulation and use a regular div when you want to manipulate stuff created in HTML. As to performance, you'll find that modern browsers are now accelerating all three, but that canvas has received the most attention so far. On the other hand, how well you write your javascript is critical to getting the most performance with canvas, so I'd still recommend using SVG.

Gaurav
  • 12,662
  • 2
  • 36
  • 34
5

They all have good things and bad things, so lets compare that below.

Canvas will have overall best performance, but only if you use it correctly.

Divs:

    • Good Performance
    • You can manipulate it using the DOM
    • You have access to DOM Events
    • CSS support
    • It's hard to make complex shapes

Performance Test Here: https://kajam.hg0428.repl.co/pref/

Canvas:

    • Better Shape Support
    • Great Performance
    • Great Browser Support
    • No CSS

Performance Test Here: https://js-game-engine.hg0428.repl.co/canvasTest/preform.html

SVGs:

    • Better Shape Support
    • Harder to use
    • Good Browser Support
    • No CSS, but lots of different SVG things
    • Bad Performance

I didn't make a performance test for this one yet, but based on other tests, its not good.

To make Canvas fast:

Canvas can have a very dynamic performance, so lets review some tips. Avoid using ctx.rect and ctx.fill, use ctx.fillRect instead, this is the biggest one, it can ruin even the most simple games. Instead of using shapes with their fill and stroke, use fill[Shape] instead.

If you dont remember that when using canvas, your games will be very slow. I learned this from experience.

Hg0428
  • 316
  • 4
  • 15
  • 1
    canvas performance test: `preform.js:36 Uncaught TypeError: Cannot set properties of undefined (setting 'type') at OnDocumentClick (preform.js:36:15)` – Jason S Feb 05 '23 at 17:12
4

While googling I find a good explanation about usage and compression of SVG and Canvas at http://teropa.info/blog/2016/12/12/graphics-in-angular-2.html

Hope it helps:

  • SVG, like HTML, uses retained rendering: When we want to draw a rectangle on the screen, we declaratively use a element in our DOM. The browser will then draw a rectangle, but it will also create an in-memory SVGRectElement object that represents the rectangle. This object is something that sticks around for us to manipulate – it is retained. We can assign different positions and sizes to it over time. We can also attach event listeners to make it interactive.
  • Canvas uses immediate rendering: When we draw a rectangle, the browser immediately renders a rectangle on the screen, but there is never going to be any "rectangle object" that represents it. There's just a bunch of pixels in the canvas buffer. We can't move the rectangle. We can only draw another rectangle. We can't respond to clicks or other events on the rectangle. We can only respond to events on the whole canvas.

So canvas is a more low-level, restrictive API than SVG. But there's a flipside to that, which is that with canvas you can do more with the same amount of resources. Because the browser does not have to create and maintain the in-memory object graph of all the things we have drawn, it needs less memory and computation resources to draw the same visual scene. If you have a very large and complex visualization to draw, Canvas may be your ticket.

Alireza Fattahi
  • 42,517
  • 14
  • 123
  • 173
0

This tool outputs HTML/CSS regular polygons instead of canvas or SVG (disclaimer, I wrote it) https://html-polygon.com

This is perhaps easier to use for interactivity as the result is real HTML elements which reside in the HTML DOM.

There are only React and Vue packages right now, but the play tool on the website exposes the resulting HTML if you don't use those and don't need dynamically generated polygon shapes.

Sam Bauers
  • 403
  • 3
  • 5