An open API service indexing awesome lists of open source software.

https://github.com/blameitonyourisp/blurrid

Generate and render blurred placeholders for lazy loaded images.
https://github.com/blameitonyourisp/blurrid

dct image-blur image-optimization lazy-loading webgl

Last synced: 3 months ago
JSON representation

Generate and render blurred placeholders for lazy loaded images.

Awesome Lists containing this project

README

        



# Blurrid

A custom [web component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) extending the `` element, providing a blurred placeholder for an image prior to loading the original image.

This repository is hosted on [github](https://github.com), if you're already reading this there, then great! Otherwise browse the repository [here](https://github.com/blameitonyourisp/blurrid). For a demo of the functionality of this repository, see [here](https://blurrid.blameitonyourisp.com).

![](https://img.shields.io/github/license/blameitonyourisp/blurrid?style=for-the-badge&labelColor=191a1a&color=779966) ![](https://img.shields.io/github/package-json/v/blameitonyourisp/blurrid/main?style=for-the-badge&labelColor=191a1a&color=779966)

### Table of Contents

- [Description](#description)
- [Getting Started](#getting-started)
- [Installation](#installation)
- [Configuration](#configuration)
- [Basic Usage](#basic-usage)
- [Encoding](#encoding)
- [Web Component](#web-component)
- [Polyfills](#polyfills)
- [Usage](#usage)
- [Encoding Options](#encoding-options)
- [Web Component Options](#web-component-options)
- [Build Integration](#build-integration)
- [Web Workers](#web-workers)
- [Node Decoding](#node-decoding)
- [Testing](#testing)
- [Documentation](#documentation)
- [Roadmap](#roadmap)
- [Attributions](#attributions)
- [License](#license)

### 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.

![](https://img.shields.io/github/repo-size/blameitonyourisp/blurrid?style=for-the-badge&labelColor=191a1a&color=779966) ![](https://img.shields.io/github/languages/code-size/blameitonyourisp/blurrid?style=for-the-badge&labelColor=191a1a&color=779966) ![](https://img.shields.io/bundlephobia/minzip/%40blameitonyourisp/blurrid?style=for-the-badge&labelColor=191a1a&color=779966) ![](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/blameitonyourisp/blurrid/main/dist/tokei.json)

## Description

Blurrid provides a custom [web component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) which extends the `` 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](https://css-tricks.com/content-jumping-avoid/) 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](https://github.com/woltapp/blurhash) project, and has been built with 3 main differences in mind:

- Blurrid renders the blurred placeholder primarily using [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) on the GPU, using [web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) 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](https://www.hostinger.co.uk/tutorials/website/improving-website-performance-using-progressive-jpeg-images) and [webp](https://developers.google.com/speed/webp)). Most browsers are shipped with support for loading and displaying these image formats (see [caniuse webp](https://caniuse.com/?search=webp) and [wikipedia image format support](https://en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support)), and as such using standard formats for image optimization in conjunction with [lazy loading techniques](https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading) 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](#usage) and [documentation](#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](https://github.com/blameitonyourisp/blurrid/blob/main/CONTRIBUTING.md).

### Installation

This package may be installed from [npm](https://www.npmjs.com/) in any appropriate javascript or typescript project. Please note that this package includes [sharp](https://www.npmjs.com/package/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:

```bash
# 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](https://jsdoc.app/), are built using the [typescript compiler](https://www.npmjs.com/package/typescript) set to emit only type declarations, and are then combined into one declaration file using [rollup](https://rollupjs.org/) with the [rollup-plugin-dts](https://www.npmjs.com/package/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](https://www.npmjs.com/package/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](#encoding) and [web component](#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:

```javascript
// 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`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/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](#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:

```html


Document



```

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`):

```javascript
// 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](https://caniuse.com/?search=web%20components) 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:

- [webcomponents](https://www.webcomponents.org/polyfills)
- [ungap](https://www.npmjs.com/package/@ungap/custom-elements)

## Usage

Please see the [basic usage](#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:

```typescript
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](https://en.wikipedia.org/wiki/Chroma_subsampling) 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.

```javascript

// 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](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_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](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images))
- 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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs) 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](https://en.wikipedia.org/wiki/Moir%C3%A9_pattern))
- 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](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) 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](https://react.dev/), nor build integrations for bundlers such as [parcel](https://parceljs.org/). 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](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) on the GPU, Blurrid provides a fallback for generating a placeholder on the CPU using [web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) (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:

```javascript
// 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

```javascript
// Import decoder.
import { BlurridDecoder, saveImage } from "@blameitonyourisp/blurrid"

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

// 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](https://jestjs.io/) for testing. See the [npm scripts section](https://github.com/blameitonyourisp/blurrid/blob/main/CONTRIBUTING.md#npm-scripts) 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](https://github.com/blameitonyourisp/blurrid/blob/main/CONTRIBUTING.md#npm-scripts) 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](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
- 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](https://react.dev/)

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

- [opensource](https://opensource.org/license/mit/)
- [choosealicense](https://choosealicense.com/licenses/mit/)
- [spdx](https://spdx.org/licenses/MIT)

#### 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.