Konva – Stage, Layer & Node snapshot and download

Getting snapshot images is straightforward once you know a couple of tricks – here they are…

TLDR: Here’s the sample code at codepen.

We’re going to look at how to get snapshots of stage, layer and nodes, but before we do here’s a bonus gift – a way to immediately download a snapshot via the browser.

How to download a snapshot image via the browser

The code to download an image is as below, pulled directly out of the Konva to data URL tutorial demo, and pasted here because you’ll want it sooner or later. 😉

// function from https://stackoverflow.com/a/15832662/512042
function downloadURI(uri, name) {
	const link = document.createElement('a');
	link.download = name;
	link.href = uri;
	delete link;

What it does should be obvious – it makes a link of the special type ‘download’ which when clicked goes into download mode for the given href. It adds the link to the document body, clicks it, and removes it from the body. The href can be an actual web URL or a hex image string. Note that upstream of this, when snapshotting the canvas, the browser will have applied the same-origin and other policies to stop this being used for stealing images of your canvases!

The sample stage

The stage looks as below. The black border is drawn at the same dimensions as the stage – so in effect it shows us the dimensions of the stage. The red boxes crossing the black boundary are deliberately drawn half outside the visible stage so that we can experiment with the target snapshot region we feed into toDataURL(). More of that in a moment!

Sample snapshot of the full stage contents

Default snapshot of the stage

The code to get a default snapshot the stage is:

$('#snapshotStageDefault').on('click', function(){
  const uri = stage.toDataURL()
  downloadURI(uri, 'Stage-snappy')

and it gets you this – notice we are not seeing the full extent of the red boxes around the edge. We are only seeing the default view which what we can see in the stage creation code. Note this is not the same as your container div element dimensions – you can experiment and discover that regardless how large you make the container, Konva will inject a canvas element per stage which has the dimensions you set in the stage creation method call. So in conclusion, without any config settings being passed into the toDataURL() call, we get whatever is visible in the stage canvas.

The snapshot returned form the default stage,getDataURL() call

What if I drag the stage or layer? Sneaky! In fact you still get the view from the canvas element viewport – not the stage (0,0) point as you might have thought. In the snapshot below I dragged the stage half way across the view.

Targeted snapshot of the stage

What if we provide a config object into the getDataURL() call? Good question – here’s what that looks like in code.

$('#snapshotStageWider').on('click', function(){
  const uri = stage.toDataURL({
    x: -100,
    y: -100,
    width: 800,
    height: 600
  downloadURI(uri, 'Stage-snappy')

And this is what you get in the snapshot.

Snapshot image resulting from passing in a config rectangle

Remember the black stroked rect is showing us where the canvas viewport is located. The conclusion then is that when we use a config object as a parameter for getDataURL() we can get more of the stage than just the part in view. That’s good news if you have to let your users snapshot the entire stage when they might be zoomed in to only a small part.

Layer snapshots

I’m not going to show you the output from the layer.toDataURL() function because it’s exactly the same as the stage versions. The only difference is at line 3 where we replaced stage with layer. And it works just like the stage version.

$('#snapshotLayerDefault').on('click', function(){
  const uri = layer.toDataURL()
  downloadURI(uri, 'layer-snappy')

Node / shape snapshots

Here we come to a departure. I came to write this blog because I was trying to snapshot a portion of an image – the project was to make an image cropper and I wanted to grab a section of the image dropping the cropped surroundings. Because, in that case, I had the co-ordinates based on the image top-left as (0,0) I kept getting weird snapshots of the stage background or random shapes. What’s the trick to getting this right – read on!

As a demo I’m going to get the node snapshot with no targeting config set, then I’ll try to set the config to snapshot the region of the circle highlighted in the image below. The will require setting all four of the config attributes.

Image showing the area we will try to snapshot

Node snapshot – default

Pretty straightforward – its the same code as per stage and layer.

$('#snapshotNodeDefault').on('click', function(){
  const uri = group.toDataURL()
  downloadURI(uri, 'group-snappy-default')

And what do we get – as expected we have the contents of the group only. Note that I used a group so that I could get the circle AND the red background rectangle. But the approach is the same for any shape – rect, circle, star, line, etc.

Group default snapshot

Targeted node snapshot

So – how do we go about hitting the target we want. Good question – and its not as simple as you might think. We can’t base our targeting on co-ordinates that use the node’s top-left as the origin.

For nodes, getDataURL() uses co-ordinates that are relative to the canvas that contains the stage.

The reason isn’t just because the node I’m using is a Konva.Group. Yes groups have a spooky nature and are always positioned at layer (0, 0) unless you reposition them, but that’s not the point. The point is that for nodes, getDataURL() uses co-ordinates that are relative to the canvas that contains the stage.

Hmmm, ok, how do we get that then? It’s relatively easy since all shapes have the getClientRect() method. [Note that there is a config object that can go into this call, you need to not give it a ‘relativeTo’ attribute for this to work, because without that the client rect is the one that is relative to the canvas, and that’s what we need.]

So the code needs to be as follows:

$('#snapshotNodeTarget').on('click', function(){
  const clientRect = group.getClientRect()
  console.log(clientRect) // logs  {x: 200, y: 100, width: 200, height: 200}
  const config = {
    x: clientRect.x + clientRect.width/2,
    y: clientRect.y + clientRect.height/2,
    width: clientRect.width/2,
    height: clientRect.height/2,
  console.log(config) // logs {x: 300, y: 200, width: 100, height: 100}
  const uri = group.toDataURL(config)
  downloadURI(uri, 'group-snappy-bottom-right-quad')

And the output we get is as expected, the bottom-right of the group contents, as shown.

Hurrah – we got what we wanted!

Using the getClientRect() code we are immune from the effect of dragging the node around, and yes we can even position the node waaaaaaay off the canvas viewport and the code still works.

What can’t we snapshot?

Anything drawn with canvas commands. What you can’t snapshot is any html element that you might have positioned above the canvas. Think about it – if the canvas could snapshot stacked html it would be a fantastic hackers toolset!


We’ve seen how to make a snapshot download, and how to get snapshots of the stage, layer and nodes. We’ve also seen that its possible to snapshot parts of the stage that aren’t in view, and how to snapshot a target area of a node.

More commonly you might get into the ‘Tainted canvases may not be exported’ error. This is about the security concern that says the browser is not allowed to take snapshots of images that came from a different source to the page that drew the canvas. Again this is to keep browser contents secure. There are various ways to solve this – research the topic ‘CORS issue’ in Google to find the latest advice.

Thanks for reading.

VW March 2023

Photo credit Marco Bianchetti at Unsplash.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: