
watermark-js-plus is a browser-based JavaScript library for placing text or image watermarks on web pages, HTML containers, and images. It also creates faint blind watermarks that can be enhanced from captured screenshots. The library works with vanilla JavaScript and frontend frameworks.
Features
- Repeating watermark tiles with adjustable dimensions and rotation.
- Multiline text and rich-text content.
- Custom grid layouts with per-cell visibility settings.
- Typography, gradients, image patterns, shadows, and Canvas filters.
- Background animation and adjustable opacity for visible watermarks.
- Runtime updates and removal through instance methods.
- Optional DOM tamper monitoring and watermark recreation.
How To Use It
Basic Usage
For a plain HTML page, load the IIFE build and call WatermarkPlus.Watermark.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.iife.min.js"></script> <script> const watermark = new WatermarkPlus.Watermark({ content: 'Internal preview', width: 240, height: 160, rotate: 25, globalAlpha: 0.16 }); watermark.create(); </script>
Installation With npm
Install the package in an application that uses an ES module bundler.
npm install watermark-js-plus
import { Watermark } from 'watermark-js-plus';
const watermark = new Watermark({ content: 'Confidential' });
await watermark.create();
Watermark a Specific Container
Set parent to an element or CSS selector. The container needs a CSS positioning context, for example position: relative.
<section id="report-preview"> <h2>Quarterly report</h2> <p>Confidential draft for internal review.</p> </section>
#report-preview {
position: relative;
min-height: 240px;
}
const reportMark = new Watermark({
parent: '#report-preview',
content: 'Internal use only',
fontColor: '#334155',
globalAlpha: 0.12
});
await reportMark.create();
Use a Logo as a Watermark
For image content, set contentType to 'image' and pass the logo URL through image. Keep the image on the same origin or configure its host to permit cross-origin loading.
const logoMark = new Watermark({
contentType: 'image',
image: '/images/company-logo.png',
imageWidth: 100,
width: 240,
height: 180,
globalAlpha: 0.2
});
await logoMark.create();
Create a Custom Grid Watermark
The grid layout can position multiple marks inside each repeated tile. Set rows, cols, spacing, and an optional visibility matrix inside gridLayoutOptions.
const gridMark = new Watermark({
content: 'DRAFT',
layout: 'grid',
width: 150,
height: 100,
gridLayoutOptions: {
rows: 2,
cols: 2,
gap: [20, 20],
matrix: [[1, 0], [0, 1]]
},
globalAlpha: 0.15
});
await gridMark.create();
Apply a Watermark to an Image
ImageWatermark writes the mark into an existing <img> element as a PNG Data URL. Call decode() after the element exists and before reading its dimensions. For a remote image, set crossorigin="anonymous" before assigning its URL and make sure the image server sends suitable CORS headers.
<img id="preview-photo" src="/images/preview.jpg" alt="Document preview">
import { ImageWatermark } from 'watermark-js-plus';
const photo = document.querySelector('#preview-photo');
await photo.decode();
const imageMark = new ImageWatermark({
dom: photo,
content: 'SAMPLE',
width: photo.width,
height: photo.height,
rotate: 0,
translatePlacement: 'bottom-end',
fontColor: '#fff',
fontSize: '28px',
globalAlpha: 0.6
});
await imageMark.create();
Create and Decode a Blind Watermark
BlindWatermark uses a fixed globalAlpha of 0.005. Its watermark is designed to be difficult to notice at normal viewing size. Decoding increases the visibility of marks in a screenshot or image, subject to the image’s color, scaling, and compression.
import { BlindWatermark } from 'watermark-js-plus';
const blindMark = new BlindWatermark({
content: 'Review copy 1042',
width: 220,
height: 160,
fontColor: '#000'
});
await blindMark.create();
Give the decoder a same-origin screenshot URL or a Data URL. It returns an enhanced PNG Data URL through onSuccess. Insert an <img id="decoded-preview" alt="Enhanced blind watermark"> element on the page to display the result.
BlindWatermark.decode({
url: '/images/captured-preview.png',
compositeOperation: 'color-burn',
compositeTimes: 3,
onSuccess: (pngDataUrl) => {
document.querySelector('#decoded-preview').src = pngDataUrl;
}
});
Update, Check, and Remove a Watermark
Use changeOptions() to update a displayed watermark. The 'append' mode retains previously configured options that do not appear in the new object.
await watermark.changeOptions({ content: 'Approved preview' }, 'append');
const exists = await watermark.check();
watermark.destroy();
Constructors
new Watermark(options): Creates a page or container watermark instance from aWatermarkOptionsconfiguration object.new ImageWatermark(options): Creates an image watermark instance. Set thedomoption to anHTMLImageElementbefore callingcreate().new BlindWatermark(options): Creates a blind watermark instance. The class overridesglobalAlphaandmode.
All Configuration Options
Size, Layout, and Position
width(number, default300): Width of a watermark tile in pixels.height(number, default300): Height of a tile in pixels.rotate(number, default45): Rotation angle in degrees.layout('default' | 'grid', default'default'): Selects regular tiling or the matrix layout.gridLayoutOptions(GridLayoutOptions | undefined, defaultundefined): Grid settings used bylayout: 'grid'.cols(number, default1): Number of columns.rows(number, default1): Number of rows.gap([number, number], default[0, 0]): Horizontal and vertical spacing.matrix(2D visibility array, default all1s): Controls which cells draw the watermark.backgroundImage(optional image): Image drawn across the grid. Supply gridwidthandheightwhen using it.width(number, optional): Grid Canvas width.height(number, optional): Grid Canvas height. These grid dimensions do not independently change the CSS background repetition size.
auxiliaryLine(boolean, defaultfalse): Displays positioning guidelines.translatePlacement(TranslatePlacementType, default'middle'): Sets the content anchor and rotation center. Values:'top','top-start','top-end','bottom','bottom-start','bottom-end','left','right','middle'.translateX(number | undefined, automatic by default): Horizontal positioning offset in pixels.translateY(number | undefined, automatic by default): Vertical positioning offset in pixels. Explicit coordinates require bothtranslateXandtranslateY. An explicitly chosen non-middletranslatePlacementtakes precedence.movable(boolean, defaultfalse): Animates the watermark background. Animation behavior depends onbackgroundRepeat.zIndex(number, default2147483647): Stacking order of the watermark overlay.parent(Element | string, default'body'): DOM element or selector containing the watermark.
Watermark Content
contentType('text' | 'image' | 'multi-line-text' | 'rich-text', default'text'): Content rendering mode.content(string, default'hello watermark-js-plus'): Text or HTML used by the text, multiline, and rich-text modes.textType('fill' | 'stroke', default'fill'): Filled or outlined Canvas text.
Typography
fontSize(string, default'20px'): Font size.fontFamily(string, default'sans-serif'): Font family.fontStyle(string, default''): Font style, for example'italic'.fontVariant(string, default''): Font variant, for example'small-caps'.fontWeight(string, default'normal'): Font weight.fontColor(string, default'#000'): Base text color.textAlign(TextAlignType | undefined, automatic by default): Horizontal text alignment. Values:'center','end','left','right','start'. The default depends on placement and coordinates.textBaseline(TextBaselineType | undefined, automatic by default): Text baseline. Values:'top','bottom','middle','alphabetic','hanging','ideographic'.lineHeight(number, default30): Line height for multiline text.textRowMaxWidth(number | undefined, defaults towidth): Canvas maximum text width and multiline wrapping threshold.letterSpacing(string, default'0px'): Character spacing.wordSpacing(string, default'0px'): Space between words.
Image and Rich Text
image(string | undefined, defaultundefined): Image URL whencontentTypeis'image'. Remote watermark images need CORS permission.imageWidth(number, default0): Width of a watermark image. Zero permits automatic sizing based on the natural dimensions andimageHeight.imageHeight(number, default0): Height of a watermark image. Zero permits automatic sizing based on the natural dimensions andimageWidth.richTextWidth(number | undefined, automatic by default): Width constraint for rich-text rendering, with a fallback towidth.richTextHeight(number | undefined, automatic by default): Height constraint for rich-text rendering, with a fallback toheight.
Effects and Background
globalAlpha(number, default0.5): Overall Canvas transparency from0to1.filter(string, default'none'): Canvas filter string. Acceptsurl(),blur(),brightness(),contrast(),drop-shadow(),grayscale(),hue-rotate(),invert(),opacity(),saturate(), andsepia().shadowStyle(Partial<CanvasShadowStyles> | undefined, defaultundefined): Canvas shadow propertiesshadowColor(string),shadowBlur(number),shadowOffsetX(number), andshadowOffsetY(number). SetshadowColorto display the shadow.advancedStyle(AdvancedStyleType | undefined, defaultundefined): Gradient or image-pattern fill, overridingfontColor.type:'linear','radial','conic', or'pattern'.params:{ x0, y0, x1, y1 }for linear gradients,{ x0, y0, r0, x1, y1, r1 }for radial gradients,{ startAngle, x, y }for conic gradients, or{ image, repetition }for image patterns. Pattern images must be preloaded.colorStops: Array of{ offset, color }objects for gradient stops.
backgroundPosition(string, default'0 0'): CSS background position.backgroundRepeat(string, default'repeat'): CSS background repetition. Animation handling includes'repeat','repeat-x','repeat-y', and'no-repeat'.
Behavior and Security
mode('default' | 'blind', default'default'): Watermark mode identifier. On a regularWatermark, selecting'blind'alone does not reduce opacity.mutationObserve(boolean, defaulttrue): Watches for modifications to watermark nodes or their direct removal and attempts to recreate the watermark.monitorProtection(boolean, defaultfalse): Activates protection against MutationObserver tampering. Once enabled, it cannot be turned off during the current runtime.
Callbacks
extraDrawFunc(Function | undefined, defaultundefined): Performs additional Canvas drawing operations.onSuccess(Function, default() => {}): Runs after the first successful creation. Redrawing does not invoke it again until an instance is destroyed and created again.onBeforeDestroy(Function, default() => {}): Runs before a watermark is removed.onDestroyed(Function, default() => {}): Runs after a watermark is removed.onObserveError(Function, default() => {}): Runs when DOM mutation observation fails.
ImageWatermark-Specific Options
dom(HTMLImageElement, required forcreate()): Image element receiving the generated PNG Data URL.destroy()restores the original image source.crossOrigin(boolean | undefined, defaultundefined): When truthy, sets the image element’scrossOriginproperty to'anonymous'. The remote server must return appropriate CORS headers.
BlindWatermark-Specific Values
BlindWatermark inherits the public Watermark options and lifecycle methods. It enforces these values even if other values are supplied.
globalAlpha(number, fixed0.005): Opacity for blind watermark rendering.mode(string, fixed'blind'): Blind watermark mode.
API Methods
create(): Creates the watermark. ReturnsPromise<void>. An existing matching watermark in the parent prevents duplicate creation.destroy(): Removes the watermark and disconnects its observers. Invokes the destroy callbacks.check(): Checks whether the watermark exists in its parent. ReturnsPromise<boolean>.changeOptions(args = {}, mode = 'overwrite', redraw = true): Updates options and returnsPromise<void>.argsisPartial<WatermarkOptions>.'overwrite'replaces previous explicit options, and'append'merges into them. Setredrawtofalseto postpone recreation.
Blind Watermark Decoder Options
BlindWatermark.decode(options) processes an image and invokes onSuccess with the enhanced PNG Data URL. Canvas export needs a same-origin image or a Data URL. The decoder does not set the image’s crossOrigin property.
url(string, default''): Source image URL or Data URL.mode('canvas' | 'html' | 'svg', default'canvas'): Decoder mode. Only'canvas'performs decoding. The other typed values are not implemented.fillColor(string, default'#000'): Fill color during compositing.compositeOperation(string, default'color-burn'): Canvas compositing mode used to reveal the faint mark.compositeTimes(number, default3): Number of compositing passes. Zero and negative values skip compositing.onSuccess(Function | undefined, defaultundefined): Callback receiving the generated PNG Data URL.
Alternatives and Related Resources
- Easily Add Custom Text Watermarks to Web Pages – Watermark-lite
- Fill Image With Diagonal Text Watermark – sfz
- JavaScript Library For Creating Watermarks On Images – watermark.js
- JavaScript Library For Creating Watermarks On Images – WatermarkIt
FAQs
Q: Why isn’t the watermark visible inside a custom container?
A: Check the selector and confirm the container has a positioning context. If the watermark exists but cannot be seen, inspect globalAlpha, the container dimensions, and zIndex.
Q: Why does watermarking an image fail when the image is hosted on another domain?
A: Cross-origin images can taint the Canvas and prevent PNG export. The image must load in anonymous CORS mode, and the remote server must permit the request. Set the attribute before loading the image.
Q: Can a watermark stop screenshots or prevent content theft?
A: No. Watermarks can discourage casual reuse and help identify shared copies, but browser code and DOM protection cannot prevent screenshots or deliberate removal.
Q: What happens if I call create() twice on the same container?
A: Watermark.create() skips creation when it finds a matching watermark in the parent. Call changeOptions() to redraw an existing instance with updated content or styles.







