Image Watermark
Stamp 'don't steal this' over your whole output, in one pass
- images
- images
ImageWatermark tiles your chosen text diagonally across an image - the classic "this is mine" watermark - and it does it as a batch operation, so it'll stamp a whole folder of generated images or every frame of a video without breaking a sweat. The README mentions a sibling video-watermarking feature; this is the image side. If you post your ComfyUI output anywhere, this is the honest way to claim it.
How it works
It's pure PIL, no model, no VRAM. The node renders your text at size (font size, 1β100, default 50) onto a small tile, rotates the tile by angle (default 30Β°, the classic anti-theft diagonal), then pastes copies of it across the image in a grid spaced by space (the gap between watermarks). The color comes from red / green / blue values (0β255 each, default black), and opacity (0β1) controls how see-through the whole thing is. Default opacity is 1.0 - fully solid - which is almost certainly more aggressive than you want; drop it to 0.15β0.3 for something that doesn't wreck the image.
The interesting bit is movement_type. On a single image it does nothing, but the node processes the whole batch (images.shape[0] frames), and these modes animate the watermark across frames:
- None - static, same position every frame.
- up_down / left_right - the watermark drifts along an axis using a sine wave, so a video watermark slides rather than sits frozen (frozen = trivially cropped out).
- angel_change - the angle rotates gradually across frames (the author's "angel" typo, but it means angle).
movement_amount (0.2β5) is the per-frame step. Leave movement on None for stills; reach for up_down/left_right if you're watermarking video.
The inputs and outputs
One input worth flagging: font_file, a plain text path. Leave it empty and it uses a bundled Chinese-friendly font (ChironGoRoundTC-600SB.ttf) - set it to a .ttf path if you want your brand font or a font that handles your language's glyphs. The default text is @ζζθ§η (the author's own handle) - change it to your mark, because nobody wants someone else's handle on their output.
Output is images, same batch shape as the input - one IMAGE in, one IMAGE out, frame count preserved. Easy to chain after Load Image @MW or before a Save node.
How to install
Part of billwuhao/ComfyUI_PortraitTools:
cd ComfyUI/custom_nodes
git clone https://github.com/billwuhao/ComfyUI_PortraitTools.git
cd ComfyUI_PortraitTools
pip install -r requirements.txt # or: ./python_embeded/python.exe -m pip install -r requirements.txt
Good news: this node needs no models at all - just huggingface_hub, opencv-python and PyYAML from requirements.txt.
Where people get burned
- The default text and default opacity are wrong for you. Solid-black
@ζζθ§ηat opacity 1.0 across a photo is not a watermark, it's a crime scene. Set your own text and get opacity down. - A missing glyph renders as a box. If your text isn't in the bundled font (or your custom font), you'll get tofu boxes instead of characters - that's what
font_fileis for. - Font path must be a real absolute path on your machine; there's no file picker here.
- movement modes do nothing on stills. One image, "None" is the only sensible choice - the animation is purely a frame-count thing.
- It doesn't remove watermarks, obviously, and it won't survive a crop - that's not a bug, that's why the movement modes exist.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | β | |
| text | STRING | @ζζθ§η | β |
| font_file | STRING | β | |
| angle | INT | 300β360 | β |
| red | INT | 00β255 | β |
| green | INT | 00β255 | β |
| blue | INT | 00β255 | β |
| opacity | FLOAT | 1.00β1 | β |
| size | INT | 501β100 | β |
| space | INT | 751β150 | β |
| movement_type | COMBO | None | 4 options: None, up_down, left_right, angel_change |
| movement_amount | FLOAT | 1.00.2β5 | β |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | β |