Convert Image to Data URI with JavaScript

By  on  

Whenever I go on a "performance run" on a website, the first place I look is imagery.  Why?  Because you can save an image out of Photoshop, push it into ImageOptim or even TinyPNG, and save 70% on its file size.  What do most developers not consider?  Taking tiny image files and making them data URIs instead of traditional images (another HTTP request).  Unfortunately that needs to happen on the CSS file before page load, but you need to get that data URI from somewhere, right?

I'm a bit suspicious of random websites which allow you upload files or content and return a given result;  you don't know the author of said code.  So I've gone to my own code, modifying it a bit along the way, to create a utility for converting an image to data URI!

Convert Image to Data URI

Like my original post, we need to rely on canvas to do the heavy lifting:

function getDataUri(url, callback) {
    var image = new Image();

    image.onload = function () {
        var canvas = document.createElement('canvas');
        canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size
        canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size

        canvas.getContext('2d').drawImage(this, 0, 0);

        // Get raw image data
        callback(canvas.toDataURL('image/png').replace(/^data:image\/(png|jpg);base64,/, ''));

        // ... or get as Data URI
        callback(canvas.toDataURL('image/png'));
    };

    image.src = url;
}

// Usage
getDataUri('/logo.png', function(dataUri) {
    // Do whatever you'd like with the Data URI!
});

You could also set this up to use Promises instead of a callback.

Once the image has loaded, we thrust it into canvas and then export its data to a data URI.  In practical terms, this isn't a useful task since the image has already loaded but if you're looking to create a local utility to perform this task, here you go!

Recent Features

  • By
    Create a CSS Flipping Animation

    CSS animations are a lot of fun; the beauty of them is that through many simple properties, you can create anything from an elegant fade in to a WTF-Pixar-would-be-proud effect. One CSS effect somewhere in between is the CSS flip effect, whereby there's...

  • By
    CSS Animations Between Media Queries

    CSS animations are right up there with sliced bread. CSS animations are efficient because they can be hardware accelerated, they require no JavaScript overhead, and they are composed of very little CSS code. Quite often we add CSS transforms to elements via CSS during...

Incredible Demos

  • By
    Dynamically Load Stylesheets Using MooTools 1.2

    Theming has become a big part of the Web 2.0 revolution. Luckily, so too has a higher regard for semantics and CSS standards. If you build your pages using good XHTML code, changing a CSS file can make your website look completely different.

  • By
    CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

Discussion

  1. Notice this only works with images on the same domain.

    • …unless you setup the correct CORS headers and set the img.crossOrigin = "Anonymous" that is.

  2. I _think_ there are existing grunt, and gulp tasks that will automate this process for you, if you’re into that sort of thing:

    Gulp: https://www.npmjs.com/package/gulp-image-data-uri
    Grunt: https://github.com/ahomu/grunt-data-uri

    BTW, thanks for this post, I love site optimization. I generally just avoid images, or use SVGs, and I did not know about this.

    Saves connections, but it seems like it’s a trade off, as overall image size will allegedly increase by 37%: http://stackoverflow.com/questions/11402329/base64-encoded-image-size

    Nevertheless, I can think of numerous cases where I’d make that trade!

    • This example shows how to do things in real time in the browser. It’s a completely different use case than doing them within a build process like Gulp or Grunt.

  3. MaxArt

    Technically, if you remove the data:image/png;base64, part you’re not getting a data URI, but the image’s raw data converted in Base64 – which can be done with a simple AJAX request and window.btoa() if you’re not resizing it or changing its format.

    The good tricks of the canvas technique are resizing, changing format, adding simple captions and getting the first frame of an animated gif.

  4. Thanks for the write-up, David. How are you dealing with alt text in this situation?

    • The same way, alt text wouldn’t change.

    • Tim

      I guess I’m asking: where is the alt text in the HTML output if you’re spitting out a canvas element? As far as I know canvas doesn’t take alt text (I could be wrong)

  5. Drew

    I like gulp-base64, which is similar gulp-image-data-uri. One nice thing it offers is the ability to set a cutoff size on images so you don’t end up with huge rasters in your css. It also lets you ignore certain files by regex.

  6. JamesG

    If only this would bring me closer to being able to have an image and imagemap as a single entity, to enable a single fetch across domain. Any thoughts welcome.

  7. Charles

    when changing canvas width and height the image is cropped, how to keep the image as is but change its width from 1024 to 600 ?

  8. GR

    Horrible function structure! You should remove the useless “callback” parameter and just return the data URI at the end of your function. And not to mention that “callback” is a special reserved word in some languages that can be embedded into JavaScript. ie: node.js.

    • How do you propose simply returning the data URI when it require the load event first? Had you argued using a Promise, I’d agree. But I’d love to see your version.

  9. As you mentioned in the end, the example is not terribly useful …but I like that you show people how to do things without starting by downloading 55 dependencies.

    When it comes to usefulness, this technique makes sense the other way around. On our site, we send images grabbed from canvas with toDataURL() back to the server to save them. We’re not sending them directly as base64 though – they are first converted to a Blob to save some space.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!