How to Create Watermarks with Sharp in Node.js: A Step-by-Step Guide
Have you ever wondered how websites automatically generate watermarks on uploaded images? If you're curious about how it's done, you're in the right place! In this tutorial, I will guide you on how to achieve this using Node.js and the popular image-processing library, Sharp.
Note that this project is a personal pet project of mine, built for my own use. It may include a few images related to the project.
I'm using Node.js v20.11.1 for this project, but feel free to use any version.
Project Setup
To start, I'll create a new project in a folder named watermark
:
npm init -y
Next, we'll install the Sharp
package with npm:
npm i sharp
Project Structure
The project contains several files that need to be created and they are assets
, output
, src
.
📦watermark
┣ 📂assets
┃ ┣ 📜01.jpg
┃ ┣ 📜02.jpg
┃ ┗ 📜watermark.png
┣ 📂output
┃ ┣ 📜01-with-watermark.png
┃ ┗ 📜02-with-watermark.png
┣ 📂src
┃ ┗ 📜watermark.generator.js
┣ 📜.gitignore
┣ 📜index.js
┣ 📜LICENSE
┣ 📜package-lock.json
┣ 📜package.json
┗ 📜readme.md
assets
will manage the assets that need to be watermarked and output
will store the watermarked images. src
is for storing all the codes related to the project.
Code Implementation
We will follow the class-based approach of JavaScript for the project. Under the src
folder, we will create a file named watermark.generator.js
. First of all, we will create a class named WatermarkGenerator
and it will be exported.
import sharp from 'sharp';
import path from 'path';
export class WatermarkGenerator {
async generateWatermark(image, watermark) {
const imageMetadata = await sharp(image).metadata();
const watermarkMetadata = await sharp(watermark).metadata();
const newWidth = Math.floor(imageMetadata.width * 0.2);
const newHeight = Math.floor(imageMetadata.height * 0.2);
// Resize the watermark image to the new dimensions
const resizedWatermark = await sharp(watermark)
.resize(newWidth, newHeight)
.toBuffer();
const { left, top } = await this.#calculateWatermarkPosition(
image,
resizedWatermark
);
await sharp(image)
.composite([
{
input: resizedWatermark,
top: top,
left: left,
},
])
.toFile(this.#pathGenerator(image));
}
async bulkGenerateWatermark(images, watermark) {
const promises = images.map((image) =>
this.generateWatermark(image, watermark)
);
await Promise.all(promises);
if (promises) console.log("Watermarks generated successfully");
}
#pathGenerator(image) {
return `./output/${path
.basename(image)
.replace(
".png" || ".jpg" || ".jpeg" || ".webp" || ".gif" || ".svg",
""
)}-with-watermark.png`;
}
async imagMetadata(image) {
return await sharp(image).metadata();
}
async #calculateWatermarkPosition(image, watermark) {
const { height, width } = await this.imagMetadata(image);
const { height: watermarkHeight, width: watermarkWidth } =
await this.imagMetadata(watermark);
const left = Math.floor(width - watermarkWidth - 20);
const top = Math.floor(height - watermarkHeight - 20);
return { left, top };
}
}
Code Explanation
We define a class named WatermarkGenerator
which will handle the watermarking process.
generateWatermark Method: This method takes an image and a watermark as input. It retrieves the metadata of both the image and the watermark, resizes the watermark to 20% of the image's dimensions (you can change the value as per your need), calculates the position where the watermark should be placed, and then composites the watermark onto the image. The final watermarked image is saved to the output directory.
bulkGenerateWatermark Method: This method allows for bulk processing of images. It takes an array of images and a watermark and applies the watermark to each image concurrently using
Promise.all
.#pathGenerator Method: This private method generates the output path for the watermarked image. It ensures the new file name is based on the original image name with a suffix indicating it has a watermark.
imagMetadata Method: This method retrieves the metadata of an image using Sharp.
#calculateWatermarkPosition Method: This private method calculates the position where the watermark should be placed on the image. It positions the watermark 20 pixels from the bottom-right corner of the image (you can change the value as per your need).
Now we will create a new file named index.js
and we will try to upload bulk images from the assets
folder and it will generate the watermarked image for us.
import path from "node:path";
import fs from "node:fs/promises";
import { WatermarkGenerator } from "./src/watermark.generator.js";
const Watermark = new WatermarkGenerator();
const readImages = async (folderPath) => {
const files = await fs.readdir(folderPath);
const filePaths = files
.filter((file) => path.basename(file) !== "watermark.png")
.map((file) => path.resolve(folderPath, file));
return filePaths;
};
const files = await readImages("assets");
await Watermark.bulkGenerateWatermark(files, watermark);
In the index.js
file, the process of generating watermarked images in bulk is as follows:
Import necessary modules such as path
and fs/promises
, and the WatermarkGenerator
class from watermark.generator.js
. Now create an instance of the WatermarkGenerator
class. Define an asynchronous function readImages
that reads all files from the assets
folder, filters out the watermark image (watermark.png
), and returns the absolute paths of the remaining images. Call readImages
to get the list of image file paths from the assets
folder. Use the bulkGenerateWatermark
method of the WatermarkGenerator
instance to apply the watermark to all the images concurrently.
I have uploaded two images to my asset folder.
and a watermark
Now let’s run the project.
node index
If the process is successful it will show the message like Watermarks generated successfully
.
Now let’s check the watermarked images. Go to the output
folder and you will find all the watermarked images. For me they are,
Conclusion
In this tutorial, we've walked through the process of creating watermarks on images using Node.js and the Sharp library. By setting up a project, organizing the necessary files, and implementing a class-based approach, you can efficiently watermark images either individually or in bulk. This method ensures that your images are protected and branded, making it a valuable tool for personal projects or professional use. With the provided code and steps, you should now be able to generate watermarked images seamlessly.
You can visit the GitHub of the project as well as the original image creator.
Subscribe to my newsletter
Read articles from M. H. Nahib directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
M. H. Nahib
M. H. Nahib
Hi, I am Nahib. Working as a software engineer at ImpleVista.