Skip to content

blameitonyourisp/blurrid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Blurrid

A custom web component extending the <img> element, providing a blurred placeholder for an image prior to loading the original image.

This repository is hosted on github, if you're already reading this there, then great! Otherwise browse the repository here. For a demo of the functionality of this repository, see here.

Table of Contents

Size

Approximate download size of repository, code files within repository, compressed main file, and (just for fun) lines written by the developer including comments etc. Please note that due to file compression, and post download installs/builds such as node module dependencies, the following badges may not exactly reflect download size or space on disk.

Description

Blurrid provides a custom web component which extends the <img> element, and provides a blurred placeholder image from a string of url-safe base64 characters. The encoded string may be any length depending on the desired blur detail, but a string of length 64 generally provides a good balance between string length and blur quality. This is done to:

  1. Increase loading speeds of websites with lots of images
  2. Save bandwidth on images which needn't be loaded since they are below the fold
  3. Prevent content jumping as images are loaded in
  4. Provide an aesthetically pleasing placeholder blur until the original content is requested and loaded

Blurrid was inspired by the blurhash project, and has been built with 3 main differences in mind:

  • Blurrid renders the blurred placeholder primarily using WebGL on the GPU, using web workers as a backup
  • Blurrid provides a custom web component, and never renders a canvas to the DOM, meaning that the blurred image can be targeted by the same scripts and styles, and will always be an img element in the DOM
  • Blurrid simplifies the initial blur calculation for images of different aspect ratios, requiring only max sample number and desired output string length

Please note that image formats specifically designed for optimising images for the web already exist (primarily progressive jpeg and webp). Most browsers are shipped with support for loading and displaying these image formats (see caniuse webp and wikipedia image format support), and as such using standard formats for image optimization in conjunction with lazy loading techniques will always be objectively more efficient with respect to bandwidth usage than Blurrid. Instead, Blurrid is intended as an option which also lends an aesthetic look to your pages before images are loaded, it's primary use being that it provides a smooth blurred image placeholder rather than a grainy intermediate image like results which may be found whilst a progressive jpeg is loading.

Getting Started

Please see the following sections for information on getting started using this repository. Below you will find information on how to install, configure, and use the main features of the repository. See the usage and documentation sections for more detailed information on how to use all features of the repository. For more information on editing or contributing to this repository (for instance repository directory structure and npm scripts), please see the CONTRIBUTING.md file here.

Installation

This package may be installed from npm in any appropriate javascript or typescript project. Please note that this package includes sharp as an external dependency. To use the loadImage and saveImage utility methods in this package (required for encoding images from local files), you must also install sharp as a dev dependency as shown below. To install the package, please use the following command:

# install sharp as external dev-dependency
npm install --save-dev sharp

# install blurrid (recommended as a normal dependency)
npm install --save @blameitonyourisp/blurrid

Types for this package are written using jsdoc, are built using the typescript compiler set to emit only type declarations, and are then combined into one declaration file using rollup with the rollup-plugin-dts plugin. This declaration file is exported with the package by default, and may be found by following the types field defined in the package.json file.

Configuration

This package requires no configuration, and should be handled by any bundler you may be using to produce the production version of scripts for your site. Please note that sharp (used for loading images to encode) is treated as an external dependency by this package, and is not bundled into the final output code.

Basic Usage

To use Blurrid for image optimization, the following steps musst be completed (pleases see the encoding and web component sub-headings below for instructions how how to carry out these steps):

  • Generate an encoded Blurrid string from a given image
  • Pass encoded string to custom blurrid-image web component in html page
  • Add blurrid-image web component by defining new custom element

Encoding

To encode a given image to a Blurrid encoded string, you must first create a new BlurridEncoder instance, passing a image buffer object to the constructor. The image buffer object can be created using the loadImage utility function called with the path of the given image. To get the encoded string, simply call the toString method on the created BluridEncoder instance. Please see the code block below for an example:

// Import encoder.
import { BlurridEncoder, loadImage } from "@blameitonyourisp/blurrid"

// Instantiate encoder with an image loaded from a path relative to point of
// execution (current working directory).
const encoder = new BlurridEncoder(await loadImage("./path/to/image"))

// Encode image to blurrid string using default arguments (no configuration
// required). The returned string will be used by the blurrid web component to
// render a placeholder image blur.
const string = encoder.toString()
console.log(`encoded-string: ${string}`)

Web Component

To add a custom blurrid-image web component to the page, you must add a standard img component with the is attribute set to the name of the custom component (blurrid-image). The img component should have the attributes set as follows (please see the web component options section for more options):

  • is attribute set to blurrid-image
  • src attribute should remain unset
  • Data attribute data-src set to original image source
  • Data attribute data-blurrid set to the generated Blurrid encoded string
  • All other attributes such as alt etc. should be set as normal for a standard img element

See the following code block for an example page containing one blurrid-image custom web component:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>
</head>
<body>
    <!-- Add custom `blurrid-image` web component. -->
    <img
    is="blurrid-image"
    data-src="<ORIGINAL_IMAGE_SOURCE>"
    data-blurrid="<BLURRID_ENCODED_STRING>" />

    <!-- Load script containing web component definition. -->
    <script type="module" src="./path/to/script"></script>
</body>
</html>

The customElements.define method must be used in the linked javascript file to define the custom blurrid-image web component. See the following code block for an example of how to define the custom web component (note that the component is imported from @blameitonyourisp/blurrid/web, not @blameitonyourisp/blurrid):

// Import custom web component.
import { BlurridImage } from "@blameitonyourisp/blurrid/web"

// Define custom element, first argument (component name) must be equal to the
// name used in the html `is` attributes.
customElements.define("blurrid-image", BlurridImage, { extends: "img" })

Polyfills

Unfortunately, at the time of writing this package, not all major browsers natively support custom web components. See caniuse for more detailed information on which browsers have still not fully implemented custom web components (mainly Safari, particularly Safari for iOS). To provide support for these browsers, please use an appropriate web components polyfill on any page where you intend to use the custom blurrid-image web component. See the following list for popular available polyfills:

Usage

Please see the basic usage section above for information on getting started with generating encoded Blurrid strings and rendering placeholder image blurs, otherwise see below for more information on all features available in this package.

Encoding Options

Various options are available for configuring both the encoded Blurrid string, and the downsized sample image which it encodes. Please see the following code block for a fragment of the type declaration for this package. The fragment shows the available parameters for the classes, methods and functions used to produce an encoded Blurrid string, and their associated jsdoc type annotations/comments:

declare class BlurridEncoder {
    /**
     * Create encoder instance from downsized image sample.
     *
     * @param {object} obj - Object destructured argument.
     * @param {Buffer} obj.buffer - Buffer of flat, 4-channel rgba pixel data.
     * @param {number} obj.width - Width of downsized sample image for blur.
     * @param {number} obj.height - Height of downsized sample image for blur.
     */
    constructor({ buffer, width, height }: {
        buffer: Buffer;
        width: number;
        height: number;
    });
    /**
     * Encode downsized sample image into serialized string containing Blurrid
     * dct coefficients.
     *
     * @param {object} obj - Object destructured argument.
     * @param {number} [obj.length=64] - Desired length of encoded string.
     * @param {string} [obj.subsampling="4:2:2"] - Chroma subsampling string of
     *      the form "Y:Cb:Cr" where Y, Cb, Cr are all 3-bit numbers from 0-7
     *      inclusive, corresponding to the sampling rates of luma, chroma blue
     *      and chroma red dct coefficients in the encoded string.
     * @returns {string}
     */
    toString({ length, subsampling }?: {
        length?: number | undefined;
        subsampling?: string | undefined;
    }): string;
    #private;
}

/**
 * Load image from file, returning buffer of flat, 4-channel rgba pixel data,
 * and metadata pertaining to the image which is required for.
 *
 * @param {string} path - Relative path to image from point of execution.
 * @param {number} [samples=16] - Maximum dimension of downsized sample image in
 *      any direction. Note that, due to max uniform limits in webgl fragment
 *      shaders, sample sizes greater than 16 will prevent a blur from being
 *      created using the webgl canvas. In this case the web component will
 *      set the image loading to `eager`, or will use web workers as a fallback
 *      if available to render the blur.
 * @returns {Promise.<{buffer:Buffer, width:number, height:number}>} Data buffer
 *      and image metadata.
 */
declare function loadImage(path: string, samples?: number | undefined): Promise<{
    buffer: Buffer;
    width: number;
    height: number;
}>;

See the following list for further information regarding choosing appropriate values for the available parameters shown in the code block above:

  • Notes on toString method length object destructured parameter:
    • Longer length will preserve more of the original blur data, and therefore produce a better decoded blur placeholder
    • Longer length will incur higher bandwidth, and marginally higher compute time when decoding
    • Generally the default value of 64 provides a good balance between string length and blur quality
  • Notes on toString method subsampling object destructured parameter:
    • Generally both chroma values should be the same
    • Higher relative luma values correspond to preservation of more object detail
    • Higher relative chroma values correspond to preservation of colour
    • Generally preserving colour is preferable for a blur placeholder where most discernable details will be lost anyway, therefore a chroma subsampling value of between 4:2:2 and 4:4:4 is recommended
    • See here for more information on chroma subsampling

Finally, see the following code block for an example using all of the available parameters as described above to configure a specific Blurrid string. Note that all of the data required to decode strings is encoded within the string itself, therefore it is possible to have strings with different encoding configurations being rendered on the same site without any need for further changes.

// Instantiate encoder using a loaded image with a custom downsized max size.
const encoder = new BlurridEncoder(await loadImage("./path/to/image", 8))

// Encode string using a custom length and subsampling string.
const string = encoder.toString({ length: 128, subsampling: "4:3:3" })

Web Component Options

The blurrid-image custom web component can be configured using a variety of data attributes. Please see the following list for converting attributes from standard img elements to blurrid-image elements, and see the following table for a brief overview of all the available data attributes for configuring the component.

  • The src attribute must not be set, and is replaced by the data-src data attribute which should hold the original source url
  • The srcset attribute must not be set, and is replaced by the data-srcset attribute which should hold the original source set (find out more about the srcset attribute here)
  • Any sizes attribute will be overridden if the optional data-size data attribute is set
  • By default the blurrid-image custom web component is compatible with (i.e. will not change/interrupt) the behaviour of all other standard img element attributes (ignoring src and srcset which must not be set)
Data Attribute Required Description Expected String Value
data-src true Original src attribute of img element Url
data-srcset true Original srcset attribute pf img element Comma-separated url list
data-blurrid true Encoded Blurrid string Url-safe base 64 string
data-size false Max size of canvas in any direction when generating placeholder blur Number
data-loading false Loading method One of eager, lazy, or onclick

Please see the list below for considerations when configuring a blurrid-image custom web component using the optional data attributes:

  • Data attribute data-size notes:
    • This data attribute sets the maximum size in any direction of the canvas used to generate the data url for the placeholder blur
    • A lower maximum canvas size will result in faster generation, however very low values may also produce repeated visual pattern artifacts in the placeholder image (these artifacts will appear much like moire patterns)
    • Values above about 512 may start to render slowly particularly on older or mobile devices
    • Values below about 16 will start to exhibit the artifacts mentioned above, whilst values above 32 will have practically no discernable artifacts
    • Particularly if the original image is high resolution, it is advised that the data-size data attribute is set to a value of around 64
    • Setting this data attribute will override the expected behaviour the standard img element sizes attribute
    • Since the sizes attribute on the standard img element also has the effect of resizing the blur canvas, it is advised that the data-size data attribute is not set when the normal sizes attribute is in use
  • Data attribute data-loading notes:
    • This data attribute determines the loading method which will be used when fetching the original source image
    • If the data-loading data attribute is not included, the default loading method will always be eager
    • Loading method eager will load the original image immediately
    • Loading method lazy will load the original image once the blurred placeholder enters the viewport (the intersection observer is configured to load the original image when over half of the blurred placeholder is in the viewport)
    • Loading method onclick will load the original image when the blurred placeholder is clicked (if you use this method, it is advised that you include a figcaption element or similar to indicate that the image must be clicked to load the original)
    • To save bandwidth, it is recommended that you override the data-loading default with the lazy loading method

Build Integration

Blurrid currently provides no plugins for frameworks such as react, nor build integrations for bundlers such as parcel. These features may be added in future versions, but currently projects using Blurrid must provide their own solutions for converting standard img elements into custom blurrid-image web components.

Web Workers

Should a given device be unable to produce a placeholder blur for an image using WebGL on the GPU, Blurrid provides a fallback for generating a placeholder on the CPU using web workers (Blurrid uses web workers in this instance to avoid blocking the main process). To enable this feature, simply import BlurridImageWorker rather than BlurridImage when defining the custom web component in your javascript files as demonstrated in the code block below:

// Import custom web component with web worker backup.
import { BlurridImageWorker } from "@blameitonyourisp/blurrid/web"

// Define custom element, with new imported component.
customElements.define("blurrid-image", BlurridImageWorker, { extends: "img" })

Node Decoding

If required, Blurrid provides a method to decode images directly. The resultant image data can be used as required, or saved directly to disk as a blurred version of the original image. To decode an encoded Blurrid string, the following steps must be completed (see the code block below for an example implementation):

  • Create new BlurridDecoder instance, passing an encoded Blurrid string to the constructor
  • Optionally resize output image to reduce compute time of blur
  • Save image directly from decoder instance, or get image data as a flat array of rgba values
// Import decoder.
import { BlurridDecoder, saveImage } from "@blameitonyourisp/blurrid"

// Instantiate decoder with an encoded string.
const decoder = new BlurridDecoder("<BLURRID_ENCODED_STRING>")

// Optionally resize the decoder metadata. Reducing the size of the output blur
// will reduce compute time. Number passed will be max dimension in width or
// height direction of the generated image blur.
decoder.metadata.resize(512)

// Directly save image from decode instance, or optionally get image data
// formatted as a flat array of rgba data.
saveImage(decoder, "./path/to/blurred/image")
const imageData = decoder.toImageData()

Testing

This repository uses jest for testing. See the npm scripts section of the repository CONTRIBUTING.md file to see the scripts available for scoped test suites. Alternatively run npm test to run all available tests.

Documentation

This repository is documented using a mixture of inline comments, jsdoc, and custom markdown tutorials for demonstrating specific functionality. For generating documentation from jsdoc comments in this repository, please see the npm scripts section of the repository CONTRIBUTING.md file, or run npm run docs:jsdoc. For markdown documentation files on specific functionality and features of this repository, please see the ./docs directory.

Roadmap

If you find a bug or think there is a specific feature that should be added or changed, please file a bug report or feature request using this repository's issue tracker. Otherwise, please see below for proposed new features which may be added in later updates:

  • Support for picture elements
  • Support for encoding images from a url
  • Built-in support for browsers which currently do not fully implement web components
  • Parser to automatically convert img elements into blurrid-image components, returning a new static html page
  • Integrations for javascript frameworks such as react

Attributions

At the time of last update, this repository used no 3rd party assets or libraries other than those which are referenced as dependencies and/or dev dependencies in the package.json file in the root of this repository. The author will endeavour to update this section of documentation promptly as and when new 3rd party assets or libraries not referenced in the package.json file are added to this repository.

License


DISCLAIMER The author(s) of this repository are in no way legally qualified, and are not providing the end user(s) of this repository with any form of legal advice or directions.


Copyright (c) 2024 James Reid. All rights reserved.

This software is licensed under the terms of the MIT license, a copy which may be found in the LICENSE.md file in the root of this repository, or please refer to the text below. For a template copy of the license see one of the following 3rd party sites:

License Text

Copyright 2024 James Reid

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.