Fabric JS Tainted Canvas – Cross-Origin Images

tainted canvas with fabric js
How to ensure cross-origin images do not taint the canvas when using Fabric JS

I’ve seen this question a few times, so thought I’d cover the topic here.

The issue is that you are using Fabric JS and all seems fine.  Then you attempt to run a function like toDataUrl to save the canvas to an image, and the browser won’t allow it.  You will see in the console an error that says something to the effect that the Canvas is Tainted.  The root of the issue is that you have an image on the canvas that was loaded cross-origin.  This violates security polices put in place by web browsers and thus you end up with a tainted canvas.  The good news is it’s possible to handle this – read on.

The Fabric JS Library

The issue is highlighted in this bug report.  And if you read through the entire thread there, you will see that it appears the issue has been resolved as of version 1.4.0 (November 2013).

So if the issue has been fixed, why am I posting this?  Well, if you are like me (and you might be since you’re reading this) you are using an older version of Fabric JS for one reason or another.  And for me, I do not want to upgrade my version of Fabric JS at this time for reasons that aren’t important.

So this post is how to cope with the problem if you also want to keep your Fabric JS library as is.

What is Tainting the Canvas?

https://github.com/kangax/fabric.js/issues/263

What is Cross-Origin Content

I won’t go into all the details because this is documented elsewhere on the internet, but simply put cross-origin means that the content was loaded from another domain.  So if your website is responding at:

and your images are loading from ANY of the following variations:

https://www.example.com/

https://example.com/

http://www.example.us/

https://www.example.us/

https://github.com/kangax/fabric.js/issues/263

https://github.com/kangax/fabric.js/issues/263

How to Not Taint a Canvas with Cross-Origin Content

Cross-origin content can be used in a canvas, even with Fabric JS, however you need to ensure two conditions are loaded correctly:

  1. The content loaded cross-origin needs to have the access-control-allow-origin header set when it is sent back to the browser.
  2. When loading the image to the browser, it needs to have the crossOrigin attribute set to Anonymous prior to loading it to the browser.

Setting the access-control-allow-origin Header

The access-control-allow-origin header is set on the server that is sending the image cross-origin to the browser (canvas).  If you operate this server, you will need to set this value.  If you do not operate this server, then you are at the mercy of the server and if they have set this value for you already.

This header can contain two values:

  1. A domain (origin) that is allowed to request content from this server cross-domain.
  2. * (wildcard) which means that any domain (origin) is allowed to request content cross-domain.

You can read more about this header and how to use it from Mozilla.

Load Images with crossOrigin set to Anonymous

Now that you have content being sent to your browser that is allowed cross-origin, it’s time to load it appropriately to the canvas with Fabric JS.

The best method for those of you using the newer versions of Fabric JS (after the above mentioned fix was implemented) would be to use the fromUrl method of Fabric JS.

With this method, you feed the URL into Fabric JS and it handles the rest.

However, if you are using an older version of Fabric JS you need to slightly modify this.  You first need to create the image in the browser, then add that image to the canvas via Fabric JS using the Image method.  Note the key to what his happening here though, before we call the onload method on the new image element we are setting the crossOrigin attribute to Anonymous, which is what allows the browser and the canvas to accept the cross-origin image.

I should point out that these two methods are actually identical in the process in what that are doing, it’s just a matter of if you are doing the extra effort or the library is doing the extra effort.

I hope this helps to clear up some confusion and frustrations for some other developers.  Cross-origin issues are frustrating when you are stuck, but at the root of them they aren’t all that complicated typically.  It’s just a matter of understanding what is happening and then setting the few flags that are required to make the internet talk across origins correctly.