Skip to main content

How to detect if the Webp image format is supported in the browser with JavaScript


The WebP image format, is known for providing a superior lossless and lossy compression for images around the web. Using WebP, webmasters and web developers can create smaller, richer images that make the web incredibly faster. WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller than comparable JPEG images at equivalent SSIM quality index. For more information about this format, please be sure to read its introduction by the Google Developers here. Till the date, you can't verify wheter this format is supported in the browser in JavaScript using a predefined method, however you can create your own method to do it using a little trick.

The logic to verify if the webp format is supported with Plain JavaScript is the following: the first you need is a base64 string with some image in webp format, in this case we'll use a white image of 1px that is already in this format, then this string needs to be converted into a Blob. There are many ways to convert a base64 string to a blob, however one of the most easiest is using the fetch API that is available in the browser and then calling the blob method from it. This will be already a blob that can be interpreted by the createImageBitmap function of the browser. The createImageBitmap method exists on the global in both windows and workers. It accepts a variety of different image sources, and returns a Promise which resolves to an ImageBitmap.

In this article, we'll share with you 2 methods that will help you to verify if this format is supported on your browser with different JavaScript versions.

Standard JavaScript

Using the tipical approach with callbacks that is supported everywhere (as long as the fetch API is supported)

function WebpIsSupported(callback){
    // If the browser doesn't has the method createImageBitmap, you can't display webp format
    if(!window.createImageBitmap){
        callback(false);
        return;
    }

    // Base64 representation of a white point image
    var webpdata = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoCAAEAAQAcJaQAA3AA/v3AgAA=';

    // Retrieve the Image in Blob Format
    fetch(webpdata).then(function(response){
        return response.blob();
    }).then(function(blob){
        // If the createImageBitmap method succeeds, return true, otherwise false
        createImageBitmap(blob).then(function(){
            callback(true);
        }, function(){
            callback(false);
        });
    });
}

Then you can use the method like:

// You can run the code like !
WebpIsSupported(function(isSupported){
    if(isSupported){
        console.log("Supported");
    }else{
        console.log("Not supported");
    }
});

ES2017

With ECMAScript 2017 you can simplify all the previous code using asynchronous functions instead of callbacks:

async function WebpIsSupported() {
    // If the browser doesn't has the method createImageBitmap, you can't display webp format
    if (!self.createImageBitmap) return false;
  
    // Base64 representation of a white point image
    const webpData = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoCAAEAAQAcJaQAA3AA/v3AgAA=';
    
    // Retrieve the Image in Blob Format
    const blob = await fetch(webpData).then(r => r.blob());

    // If the createImageBitmap method succeeds, return true, otherwise false
    return createImageBitmap(blob).then(() => true, () => false);
}

And you can test if the format is supported like:

// You can run your code like
(async () => {
    if(await WebpIsSupported()) {
        console.log('Is Supported');
    } else {
        console.log("Isn't supported");
    }
})();

Redundancy? I think yes.

Although till this point we don't have a real use-case more than to optimize image loading on application, this implementation is more informative as it can provide an idea of how to check easily if the format is supported in the browser or not. The funniest thing on the initial snippet (standard JavaScript), is that if you will include the snippet on your application and this will run on older browsers, you will need as well check for support of the fetch API and an extra way to convert the base 64 string to a blob and then use that blob with the createImageBitmap method. Checking if the fetch API is supported or using a polyfill for it, will lead to another dependency, namely the support for Promises.

Comments

Popular posts from this blog

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

4 Ways to Communicate Across Browser Tabs in Realtime

1. Local Storage Events You might have already used LocalStorage, which is accessible across Tabs within the same application origin. But do you know that it also supports events? You can use this feature to communicate across Browser Tabs, where other Tabs will receive the event once the storage is updated. For example, let’s say in one Tab, we execute the following JavaScript code. window.localStorage.setItem("loggedIn", "true"); The other Tabs which listen to the event will receive it, as shown below. window.addEventListener('storage', (event) => { if (event.storageArea != localStorage) return; if (event.key === 'loggedIn') { // Do something with event.newValue } }); 2. Broadcast Channel API The Broadcast Channel API allows communication between Tabs, Windows, Frames, Iframes, and  Web Workers . One Tab can create and post to a channel as follows. const channel = new BroadcastChannel('app-data'); channel.postMessage(data); And oth...

Working with Node.js streams

  Introduction Streams are one of the major features that most Node.js applications rely on, especially when handling HTTP requests, reading/writing files, and making socket communications. Streams are very predictable since we can always expect data, error, and end events when using streams. This article will teach Node developers how to use streams to efficiently handle large amounts of data. This is a typical real-world challenge faced by Node developers when they have to deal with a large data source, and it may not be feasible to process this data all at once. This article will cover the following topics: Types of streams When to adopt Node.js streams Batching Composing streams in Node.js Transforming data with transform streams Piping streams Error handling Node.js streams Types of streams The following are four main types of streams in Node.js: Readable streams: The readable stream is responsible for reading data from a source file Writable streams: The writable stream is re...