Generating BlurHash Strings and Creating Blurred Placeholders with Node.js

Mohammed shamseer pv
5 min readSep 24, 2024

--

In this post, we’ll explore how to generate a BlurHash string from an image and use it to create a blurred placeholder image. BlurHash is an efficient way to encode an image into a short string, which can later be decoded back into a low-quality blurred version. It’s great for improving user experiences by providing placeholders while images load.

We’ll be using the following packages:

  • axios: To fetch images from URLs.
  • fs: For file system operations.
  • blurhash: To encode and decode images into a BlurHash.
  • image-size: To determine image dimensions.
  • canvas: To draw images onto a canvas and create placeholder images.

Step 1: Fetching Images

First, we need to fetch an image, whether from a URL or from the local filesystem.

async function fetchImage(url) {
const response = await axios.get(url, { responseType: 'arraybuffer' });
return Buffer.from(response.data);
}

Here, we use axios to fetch the image data as an arraybuffer and convert it into a Buffer.

Step 2: Extract Image Data

To process the image and create a BlurHash, we need to extract pixel data from it. We’ll use canvas for this, resizing the image if it’s larger than a specified maximum size.

async function getImageData(imageBuffer, maxSize = 512) {
const image = await loadImage(imageBuffer);
const scale = Math.min(maxSize / image.width, maxSize / image.height, 1);
const width = Math.floor(image.width * scale);
const height = Math.floor(image.height * scale);

const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height).data;

return {
data: new Uint8Array(imageData),
width,
height
};
}

This function:

  • Loads the image using loadImage().
  • Scales the image if necessary to fit within the maxSize constraint.
  • Draws the image onto a canvas and extracts the pixel data.

Step 3: Generating a BlurHash

Next, we take the pixel data and generate a BlurHash string using the blurhash library. The number of components (e.g., 4 and 3 in the encode() function) can be adjusted to control the level of detail.

async function generateBlurhash(imageSource, isUrl = false) {
try {
let imageBuffer;
let width, height;

if (isUrl) {
imageBuffer = await fetchImage(imageSource);
} else {
const dimensions = sizeOf(imageSource);
width = dimensions.width;
height = dimensions.height;
imageBuffer = fs.readFileSync(imageSource);
}

const { data, width: imgWidth, height: imgHeight } = await getImageData(imageBuffer);
const blurhash = encode(data, imgWidth, imgHeight, 4, 3); // Adjust components as needed

return { blurhash, imgWidth, imgHeight };
} catch (error) {
console.error('Error generating BlurHash:', error);
}
}
  • If isUrl is true, the image is fetched from the web; otherwise, it’s read from the local filesystem.
  • The getImageData() function processes the image, and the blurhash library encodes the pixel data into a BlurHash string.

Step 4: Decoding the BlurHash to Create a Placeholder

We now take the BlurHash string and decode it into an array of pixels, which is then drawn onto a canvas to create the blurred placeholder.

function createBlurredImage(blurhash, width, height, outputPath) {
const pixels = decode(blurhash, width, height);

const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');

const imageData = ctx.createImageData(width, height);
imageData.data.set(pixels);

ctx.putImageData(imageData, 0, 0);

const out = fs.createWriteStream(outputPath);
const stream = canvas.createPNGStream();
stream.pipe(out);
out.on('finish', () => {
console.log('Blurred image saved to', outputPath);
});
}

This function:

  • Decodes the BlurHash string into pixel data.
  • Draws the decoded pixels onto a canvas and saves the blurred image as a PNG file.

Example Usage

Here’s how you can put everything together and generate both the BlurHash string and the blurred image:

async function exampleGenerateBlurhash() {
const imageSource = './mohanlal.jpg'; // Path to your local image
const isUrl = false; // Set to true if imageSource is a URL

const result = await generateBlurhash(imageSource, isUrl);
if (result) {
console.log('BlurHash:', result.blurhash);
}
}

async function exampleCreateBlurredImage() {
const imageSource = './mohanlal.jpg'; // Path to your local image
const isUrl = false;
const outputPath = './blurred-placeholder.png'; // Output file path

const result = await generateBlurhash(imageSource, isUrl);
if (result) {
createBlurredImage(result.blurhash, result.imgWidth, result.imgHeight, outputPath);
}
}

exampleGenerateBlurhash();
exampleCreateBlurredImage();

In this example:

  • We first generate the BlurHash string using exampleGenerateBlurhash().
  • Then, we create a blurred placeholder image using exampleCreateBlurredImage().
const fs = require('fs');
const axios = require('axios');
const { encode, decode } = require('blurhash');
const sizeOf = require('image-size');
const { createCanvas, loadImage } = require('canvas');

// Fetch image from URL
async function fetchImage(url) {
const response = await axios.get(url, { responseType: 'arraybuffer' });
return Buffer.from(response.data);
}

// Extract image data
async function getImageData(imageBuffer, maxSize = 512) {
const image = await loadImage(imageBuffer);

// Resize if the image is larger than maxSize
const scale = Math.min(maxSize / image.width, maxSize / image.height, 1);
const width = Math.floor(image.width * scale);
const height = Math.floor(image.height * scale);

const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height).data;

return {
data: new Uint8Array(imageData),
width,
height
};
}

// Generate blur hash
async function generateBlurhash(imageSource, isUrl = false) {
try {
let imageBuffer;
let width, height;

if (isUrl) {
// Handle network image
imageBuffer = await fetchImage(imageSource);
} else {
// Handle local file
const dimensions = sizeOf(imageSource);
width = dimensions.width;
height = dimensions.height;
imageBuffer = fs.readFileSync(imageSource);
}

const { data, width: imgWidth, height: imgHeight } = await getImageData(imageBuffer);

// Encode the image data to BlurHash
const blurhash = encode(data, imgWidth, imgHeight, 4, 3); // Adjust components as needed

return { blurhash, imgWidth, imgHeight };
} catch (error) {
console.error('Error generating BlurHash:', error);
}
}

// Create a blurred placeholder image from a blur hash
function createBlurredImage(blurhash, width, height, outputPath) {
// Decode the blur hash
const pixels = decode(blurhash, width, height);

// Create a canvas and draw the decoded image
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');

// Create an ImageData object and set the pixel data
const imageData = ctx.createImageData(width, height);
imageData.data.set(pixels);

// Draw the image data onto the canvas
ctx.putImageData(imageData, 0, 0);

// Save the canvas as an image file
const out = fs.createWriteStream(outputPath);
const stream = canvas.createPNGStream();
stream.pipe(out);
out.on('finish', () => {
console.log('Blurred image saved to', outputPath);
});
}

// Example usage of functions

// Generate blur hash from a local image or URL
async function exampleGenerateBlurhash() {
const imageSource = './mohanlal.jpg'; // Path to your local image
const isUrl = false; // Set to true if imageSource is a URL

const result = await generateBlurhash(imageSource, isUrl);
if (result) {
console.log('BlurHash:', result.blurhash);
// Optionally save the blur hash to a file or database
}
}

// Create a blurred placeholder image from a blur hash
async function exampleCreateBlurredImage() {
const imageSource = './mohanlal.jpg'; // Path to your local image
const isUrl = false; // Set to true if imageSource is a URL
const outputPath = './blurred-placeholder.png'; // Output file path

const result = await generateBlurhash(imageSource, isUrl);
if (result) {
createBlurredImage(result.blurhash, result.imgWidth, result.imgHeight, outputPath);
}
}

// Run the example functions
exampleGenerateBlurhash();
exampleCreateBlurredImage();

Conclusion

In this tutorial, we’ve covered how to generate a BlurHash string from an image and use it to create a blurred placeholder image. This technique is useful for improving user experience by showing a preview while the original image loads.

--

--

Mohammed shamseer pv
Mohammed shamseer pv

Written by Mohammed shamseer pv

skilled in Flutter, Node.js, Python, and Arduino, passionate about AI and creating innovative solutions. Active in tech community projects.

No responses yet