Ultra Tools Image Watermark
Ultra Tools Image Watermark
- image
- watermark_image
- mask
- IMAGE
- MASK
Why not just paste a logo on
Because every naive implementation of "paste a logo on" has the same three bugs.
It sizes the watermark by the source canvas instead of the visible logo, so your carefully exported PNG with a 400ร400 transparent canvas around a small mark comes out microscopic. It ignores alpha, so you get a black or white box where the transparency should be. And it gives you opacity and a position, then stops - no tiling, no blend modes, no way to make a pattern that survives being cropped.
Ultra Tools Image Watermark is a serious pass at all three. It's the node I'd hand to someone putting a mark on a few hundred images, and it's the one node in this pack with an obvious answer to "why this over a Python script."
How it works
The watermark is prepared in RGBA, and - this is the good bit - it gets cropped to its alpha bounding box before being scaled. The sizing percentage therefore applies to the logo you can see, not to the canvas the designer exported. There's a Mask_Mode for how the alpha is interpreted, because ComfyUI's own Load Image convention hands you an inverted alpha mask (1 - alpha); the default load image mask (invert) follows that, and normal mask (white visible) is for a mask you produced yourself where white means "show this."
Compositing is done in chunks in torch with a documented pixel ceiling per operation, so a 4K frame with a tiled, rotated watermark doesn't quietly allocate a dozen gigabytes. When a combination genuinely can't be done safely - an enormous tile spacing combined with rotation is the classic - the node stops and tells you, rather than silently downscaling your output. I'd take that trade every time, though it does mean a scary-sounding error the first time you hit it.
Tiling has two shapes and two alignments. full is a straight grid; diagonal offsets alternate rows so the pattern reads as a weave. angle aligned rotates the tile grid with the watermark; screen aligned keeps the grid orthogonal to the frame while the mark itself rotates. For copyright-defence patterns you want diagonal plus screen-aligned plus ~30ยฐ, which is the standard stock-photo look for a reason: it's very hard to crop around.
What you set
The widget list is long because it's genuinely parametric - grouped as watermark image, stroke, shadow, tile and adjust.
The ones that matter first: watermark (a dropdown of files already in ComfyUI's input folder - there's an upload button in the node, and there's also an optional watermark_image IMAGE input if you'd rather wire the mark in from the graph), Size_Mode (percentage of the image width, default, or pixels with Pixel_Width when the mark must be a fixed size regardless of frame), Position (seven placements), Margin plus Margin_X/Margin_Y for nudges.
Then the finishing layer: Opacity, Blend_Mode (normal, multiply, screen, overlay, soft light - multiply is the one for a dark logo on light images, screen for the inverse), Stroke_Width and colour, Shadow_Enabled with offsets and blur, Edge_Softness, and Rotation.
Two optional inputs, both genuinely useful: watermark_image (an IMAGE, so you can generate or composite the mark in-graph) and mask (a MASK, so a detected region can gate the watermark).
Outputs are IMAGE and MASK. That MASK is the composed watermark region as a mask, which is the whole reason this node exists in a pipeline: wire it into a downstream inpaint or a Text Watermark node that needs to avoid the logo area.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Yamoon-jjang/comfyui-Ultra-Tools
Manager โ Ultra Tools works too. Full restart, then Ctrl+F5 - and this node is one where the hard refresh really matters, because the upload button and half the parameter UI live in web/ultra_watermark_upload.js. An empty watermark dropdown after an update is a stale-bundle symptom, not a permissions problem.
No pip packages. Pillow and numpy, both already there. If you ran an older comfyui-Ultra-* pack alongside this one, delete it first.
Common issues
Logo barely visible at 15%. Scale_Percent is a share of image width, and 15% of 1024 sounds bigger than it looks once the mark is a thin wordmark. 20โ30% is more typical; for a hard-quantity mark, switch to pixels.
Nothing appears at all. If you're loading the mark through the mask input, check Mask_Mode - inverted is the default and it will happily hide your logo.
Tiled output looks like a sheet of noise. Drop tile spacing toward the default 100 and raise opacity gradually. Very small spacing plus a busy mark at full opacity is unreadable, and the node will also start refusing the work at extreme combinations.
Recommended order for a finished image: original โ Image Watermark (tiled mark) โ Text Watermark (credit line) โ Save. And if you're posting the results, remember the PNG's metadata carries your whole graph anyway - the watermark is the part people actually see.
Inputs (38)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | โ | |
| watermark | COMBO | 2 options: , example.png | |
| โโ ๐ผ๏ธ Watermark Image โโ | STRING | โ | |
| Size_Mode | COMBO | percentage | 2 options: percentage, pixels |
| Scale_Percent | FLOAT | 15.00.5โ100 | โ |
| Pixel_Width | INT | 2001โ8192 | โ |
| Invert_Colors | BOOLEAN | false | โ |
| Color_Tint | BOOLEAN | false | โ |
| Tint_Color_Preset | COMBO | custom hex | 17 options: custom hex, white, black, red, green, blue, +11 |
| Tint_Color | STRING | #FFFFFF | โ |
| Mask_Mode | COMBO | load image mask (invert) | 2 options: load image mask (invert), normal mask (white visible) |
| โโ โ๏ธ Stroke โโ | STRING | โ | |
| Stroke_Width | INT | 00โ20 | โ |
| Stroke_Color_Preset | COMBO | custom hex | 17 options: custom hex, white, black, red, green, blue, +11 |
| Stroke_Color | STRING | #000000 | โ |
| โโ ๐ Shadow โโ | STRING | โ | |
| Shadow_Enabled | BOOLEAN | false | โ |
| Shadow_Color_Preset | COMBO | custom hex | 17 options: custom hex, white, black, red, green, blue, +11 |
| Shadow_Color | STRING | #000000 | โ |
| Shadow_Opacity | FLOAT | 0.500โ1 | โ |
| Shadow_Offset_X | INT | 4-100โ100 | โ |
| Shadow_Offset_Y | INT | 4-100โ100 | โ |
| Shadow_Blur | FLOAT | 6.00โ50 | โ |
| โโ ๐ฒ Tile โโ | STRING | โ | |
| Tile_Mode | COMBO | off | 3 options: off, full, diagonal |
| Tile_Axis_Mode | COMBO | angle aligned | 2 options: angle aligned, screen aligned |
| Tile_Spacing | INT | 100-2000โ2000 | โ |
| โโ ๐๏ธ Adjust โโ | STRING | โ | |
| Opacity | FLOAT | 1.000โ1 | โ |
| Blend_Mode | COMBO | normal | 5 options: normal, multiply, screen, overlay, soft light |
| Position | COMBO | bottom right | 7 options: center, top left, top center, top right, bottom left, bottom center, +1 |
| Margin | INT | 200โ2048 | โ |
| Margin_X | INT | 0-2048โ2048 | โ |
| Margin_Y | INT | 0-2048โ2048 | โ |
| Edge_Softness | INT | 00โ5 | โ |
| Rotation | FLOAT | 0-180โ180 | โ |
| watermark_imageopt | IMAGE | โ | |
| maskopt | MASK | โ |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | โ |
| MASK | MASK | โ |