How To Add Watermark To Your Videos With Node.js

How To Add Watermark To Your Videos With Node.js

Today we use watermarks for a wide variety of applications, with the majority of watermarks now being off the digital variety. It provides for a clear, yet relatively unobtrusive way, to show original authorship. This is particularly important in the age of the internet where it is easy to copy and appropriate media without permission.

This guide has been written to show a quick and easy way to develop an application that can add watermarks to your videos using the Shotstack API. This API allows you to describe a video edit in JSON, and then use your favorite programming language to render thousands of videos concurrently in the cloud.

Prerequisites

Shotstack API

The Shotstack API allows you to render tens of thousands of videos in the cloud, and personalize each individual video through small changes to a JSON file. With API capable of rendering hundreds of videos concurrently in the cloud. After registering just log in to receive your API key.

The free version provides you with free use of the API, but does embed a small watermark into your video. You can get rid of this by adding in your payment information and using your production key.

Node.js

We'll be using Node.js to build our application. No fancy routing, just the basics.

Setting the scene

For this example we'll be pretending we shot some great footage of an open house, and are keen to have this watermarked so prospective buyers know where to look. We'll use three video clips from Pexels that together will paint a beautiful picture of what's for sale:

As our watermark we'll be using the logo of our real estate company; Block real estate:

A simple watermark

The code

For this guide, I won't go in too much depth about how the API exactly works and what different effects and transitions are available, but if you're in need of a primer please take a quick look at this guide.

JSON

The Shotstack API works by sending a JSON string to the API endpoint. The JSON provides a timeline of clips, transitions and effects that are converted into an output file such as an MP4 or a GIF.

In the example below we composite our watermark (a PNG file) on top of the three videos. The scale, opacity, position and offset properties then allow us to position the watermark exactly where we want it to be placed.

{
    "timeline": {
        "background": "#000000",
        "tracks": [
            {
                "clips": [
                    {
                        "asset": {
                            "type": "image",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/logos/real-estate-black.png"
                        },
                        "start": 0,
                        "length": 13,
                        "fit": "none",
                        "scale": 0.33,
                        "opacity": 0.5,
                        "position": "bottomRight",
                        "offset": {
                            "x": -0.04,
                            "y": 0.04
                        }
                    }
                ]
            },
            {
                "clips": [
                    {
                        "asset": {
                            "type": "video",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/footage/suburbia-aerial.mp4"
                        },
                        "start": 0,
                        "length": 5,
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    },
                    {
                        "asset": {
                            "type": "video",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/footage/home-interior-1.mp4"
                        },
                        "start": 4,
                        "length": 5,
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    },
                    {
                        "asset": {
                            "type": "video",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/footage/home-interior-2.mp4"
                        },
                        "start": 8,
                        "length": 5,
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    }
                ]
            }
        ]
    },
    "output": {
        "format": "mp4",
        "resolution": "sd"
    }
}

You can submit this JSON to the API using Curl or an application like Postman (see our Hello World tutorial on how to use Curl to post directly to the API), but for this tutorial we will create a simple application using a Node.js script. Save the above JSON in a file called template.json which our script will read.

Node.js application

The Node.js script below takes the JSON, and sends it to the API. It then polls the API to retrieve the render status. After about 30 seconds it logs the video URL for you to use. Before running the script you will need to install the dotenv and axios libraries using npm or yarn.

require('dotenv').config();
const axios = require('axios');

const shotstackUrl = 'https://api.shotstack.io/stage/';
const shotstackApiKey = process.env.SHOTSTACK_API_KEY; // Either declare your API key in your .env file, or set this variable with your API key right here.

const json = require('./template.json');

/**
 * Post the JSON video edit to the Shotstack API
 * 
 * @param {String} json  The JSON edit read from template.json
 */
const renderVideo = async (json) => {
    const response = await axios({
        method: 'post',
        url: shotstackUrl + 'render',
        headers: {
            'x-api-key': shotstackApiKey,
            'content-type': 'application/json'
        },
        data: json
    });

    return response.data;
}

/**
 * Get the status of the render task from the Shotstack API
 * 
 * @param {String} uuid  The render id of the current video render task 
 */
const pollVideoStatus = async (uuid) => {
    const response = await axios({
        method: 'get',
        url: shotstackUrl + 'render/' + uuid,
        headers: {
            'x-api-key': shotstackApiKey,
            'content-type': 'application/json'
        },
    });

    if (!(response.data.response.status === 'done' || response.data.response.status === 'failed')) {
        setTimeout(() => {
            console.log(response.data.response.status + '...');
            pollVideoStatus(uuid);
        }, 3000);
    } else if (response.data.response.status === 'failed') {
        console.error('Failed with the following error: ' + response.data.response.error);
    } else {
        console.log('Succeeded: ' + response.data.response.url);
    }
}

// Run the script
(async () => {
    try {
        const render = await renderVideo(JSON.stringify(json));
        pollVideoStatus(render.response.id);
    } catch (err) {
        console.error(err);
    }
})();

Initial result

Once we run the Node.js application and the render is complete we should be left with the following video:

This is already looking pretty good, but the black watermark in the first scene isn't very clear. It would be helpful if we can change the watermark we use based on the scene.

Final touches

We'll add a few final touches such as transitions, a title, some tunes, and switch our watermark image colour based on the scene.

{
    "timeline": {
        "soundtrack": {
            "src": "https://feeds.soundcloud.com/stream/267703548-unminus-white.mp3",
            "effect": "fadeOut"
        },
        "background": "#000000",
        "tracks": [
            {
                "clips": [
                    {
                        "asset": {
                            "type": "title",
                            "text": "273 Murcheson Drive, East Hampton, NY",
                            "style": "future",
                            "size": "x-small",
                            "position": "bottomLeft",
                            "offset": {
                                "x": 0.6,
                                "y": -0.2
                            }
                        },
                        "start": 1,
                        "length": 5,
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    }
                ]
            },
            {
                "clips": [
                    {
                        "asset": {
                            "type": "image",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/logos/real-estate-white.png"
                        },
                        "start": 0,
                        "length": 5,
                        "fit": "none",
                        "scale": 0.33,
                        "opacity": 0.5,
                        "position": "bottomRight",
                        "offset": {
                            "x": -0.04,
                            "y": 0.04
                        },
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    },
                    {
                        "asset": {
                            "type": "image",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/logos/real-estate-black.png"
                        },
                        "start": 4,
                        "length": 5,
                        "fit": "none",
                        "scale": 0.33,
                        "opacity": 0.5,
                        "position": "bottomRight",
                        "offset": {
                            "x": -0.04,
                            "y": 0.04
                        },
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    },
                    {
                        "asset": {
                            "type": "image",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/logos/real-estate-black.png"
                        },
                        "start": 8,
                        "length": 5,
                        "fit": "none",
                        "scale": 0.33,
                        "opacity": 0.5,
                        "position": "bottomRight",
                        "offset": {
                            "x": -0.04,
                            "y": 0.04
                        },
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    }
                ]
            },
            {
                "clips": [
                    {
                        "asset": {
                            "type": "video",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/footage/suburbia-aerial.mp4"
                        },
                        "start": 0,
                        "length": 5,
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    },
                    {
                        "asset": {
                            "type": "video",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/footage/home-interior-1.mp4"
                        },
                        "start": 4,
                        "length": 5,
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    },
                    {
                        "asset": {
                            "type": "video",
                            "src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/footage/home-interior-2.mp4"
                        },
                        "start": 8,
                        "length": 5,
                        "transition": {
                            "in": "fade",
                            "out": "fade"
                        }
                    }
                ]
            }
        ]
    },
    "output": {
        "format": "mp4",
        "resolution": "sd"
    }
}

Final result

See below our final result — a professionally edited real estate video with watermarks to show off your creation!

Conclusion

The code in this guide provides a straightforward way to build an application that takes an image to act as a watermark and composite it on top of a video.

We've built a more comprehensive open-source watermarker application which you can use to watermark your videos, with the complete source code available on GitHub.

Hopefully, this article has inspired you to start using code to manipulate video. This code could easily be further adapted to use a video watermark instead of a static image, use HTML to personalize it even further, among many other manipulations at a scale that would not be possible using an old fashioned video editor.

Follow Shotstack to get similar articles about programmable videos and applications. Start with our learn resources to learn to start programmable videos. Sign up for free to start building today!