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

    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
    An Interview with Eric Meyer

    Your early CSS books were instrumental in pushing my love for front end technologies. What was it about CSS that you fell in love with and drove you to write about it? At first blush, it was the simplicity of it as compared to the table-and-spacer...

  • By
    LightFace:  Facebook Lightbox for MooTools

    One of the web components I've always loved has been Facebook's modal dialog.  This "lightbox" isn't like others:  no dark overlay, no obnoxious animating to size, and it doesn't try to do "too much."  With Facebook's dialog in mind, I've created LightFace:  a Facebook lightbox...

Incredible Demos

  • By
    CSS Custom Cursors

    Remember the Web 1.0 days where you had to customize your site in every way possible?  You abused the scrollbars in Internet Explorer, of course, but the most popular external service I can remember was CometCursor.  CometCursor let you create and use loads of custom cursors for...

  • By
    Degradable SELECT onChange

    Whenever I go to Google Analytics I notice a slight flicker in the dropdown list area. I see a button appear for the shortest amount of time and the poof! Gone. What that tells me is that Google is making their site function...


  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:


    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%:

    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!