VideoMark Video
Watermark and end cards in one node, if your ComfyUI is new enough
- video
- logo
- logo_mask
- video
VIDEO in, VIDEO out, everything done
VideoMark Video is the pack's convenience node: feed it a ComfyUI VIDEO, and it paints the watermark onto the frames, optionally glues a copyright card on the head and/or tail, keeps audio in sync, and hands you a VIDEO back. One node instead of three - no decode-to-IMAGE step, no re-encode step, and no frame rate to declare by hand, because it reads that off the video.
This is the node for the 2026 workflow shape where the model already emits a VIDEO. LTX-2 and MiniMax H3 land there, and Wan pipelines reach it through Create Video. If your graph currently ends … → Save Video, dropping this in the middle is a two-wire change.
The tradeoff of a convenience node is that it hides its seams, and this one has three: the card's text size follows the watermark's scale rather than having its own, the card never gets a stroke, and there's no separate head/tail text. If you want to style the card and the watermark independently, you want Overlay + Title chained instead. If you want "watermark plus a copyright card, don't make me think", this is the one.
No community signal to lean on either - a corpus search for "videomark" across two years of r/StableDiffusion, r/comfyui and r/civitai comes back empty, and most actual watermark discussion there is about removing marks, not adding them.
How it works
It pulls the video apart with get_components(): an IMAGE tensor plus frame_rate, audio, metadata and alpha. The watermark renderer is the same code the Overlay uses - one RGBA stamp sized to the frames, alpha-composited per frame, stroke and shadow baked in - and the card, if enabled, is title_seconds × fps frames of solid colour with text and logo laid out the same way, concatenated at the ends.
Then it rebuilds a VIDEO, preserving the input's bit depth and colour space and writing the frame rate back. One honest detail: if cards changed the frame count, the original alpha channel no longer matches the picture, so it's dropped rather than encoded wrong. Source with real transparency loses it. Everything is numpy + Pillow - no dependencies, no VRAM, no model.
The inputs you'll actually touch
video- the only required input; everything else has a default.mode(corner,center,floating,tile) withposition, thentext,opacity,scale,margin. Same rules as the Overlay:scaleis a percentage of frame width so it survives a resolution change,marginis in pixels, andcenter/tilewant opacity down around 0.12–0.22.title_mode-off(default),head,tail,both- withtitle_seconds(2 is standard) andtitle_text. Leavetitle_textempty and it inheritstext, which means onetext_inwire feeds both the watermark and the card.title_bg_colorfor the card, plusstyleand the stroke/shadow controls for the watermark, and the optionallogo/logo_mask/logo_filetrio -use_logois off by default, and a wired logo beats the uploaded file.
text_in behaves like the rest of the pack: connected and non-empty wins, blank falls back to the typed text. Output is a single video, straight into Save Video.
Install
# ComfyUI Manager: search "comfyui-videomark"
cd ComfyUI/custom_nodes
git clone https://github.com/kaerozhi/comfyui-videomark.git
Also available as comfy node install comfyui-videomark. No pip step: the pack declares zero dependencies and rides on the torch/numpy/Pillow ComfyUI already ships. Restart afterwards.
Where people get burned
The node isn't there at all. It only registers if your ComfyUI exposes the comfy_api.latest VIDEO types. The pack imports them at load time and skips the node entirely if they're missing, printing a [VideoMark] line about the failed VIDEO import to the console. On an older install you get Overlay and Title and no sign of the third node. Updating ComfyUI is the fix - it isn't a pack bug, and it's the one gotcha unique to this node.
A card bigger than you expected. Card text and logo sizes follow scale, the watermark's own percentage. Bump scale for a bigger card and you've made the watermark bigger too. Chained Overlay + Title avoids that, since Title has its own logo_scale and text_scale.
A watermark that isn't there. If rendering throws, the node logs one [VideoMark] line and passes the video through untouched. You get a clean clip, not an error - read the console before debugging your wiring.
Missing font glyphs. 黑体 (simhei) has no ©, so a copyright line renders as a box there. Use 微软雅黑 / 宋体 / 楷体, or point font_file at a font file.
Dark shadow on dark footage. Invisible by definition. Keep the stroke, or set shadow_color light - same advice as the rest of the pack.
Editing the panel without a restart. The panel under web/ is scanned at startup, so changing an existing JS file only needs Ctrl+Shift+R, while adding a new one needs a ComfyUI restart.
Inputs (39)
| Name | Type | Default | Description |
|---|---|---|---|
| video | VIDEO | — | |
| mode | COMBO | corner | Watermark mode: corner fixed in one corner (least intrusive, best default) center dead centre - keep opacity around 0.15~0.25 floating wanders inside the safe area (hardest to crop out, worst to watch) tile repeated across the whole frame - hardest to remove in post |
| position | COMBO | bottom_right | Which corner to use in corner mode. Bottom-right blocks the least; switch to top-left if the platform overlays its own UI (like/share buttons) there. |
| text | STRING | © 2026 kaero | Watermark text. Multiple lines supported - just press Enter. |
| opacity | FLOAT | 0.850.02–1 | Overall opacity. Reference values: corner 0.75~1.0 (needs to stay legible) centre / tile 0.12~0.22 (higher starts to hurt viewing) floating 0.25~0.45 (too low gets erased after compression) |
| scale | FLOAT | 20.01–100 | Watermark width as a percentage of the frame width. Resolution independent - you never retune it. 15~25 for text, 8~18 for a logo. |
| margin | INT | 320–600 | Safe margin from the frame edge, in pixels. Floating and tiled stamps respect it too, so nothing ever gets clipped. 24~40 for 736x992; 36~64 for 1080p. |
| use_text | BOOLEAN | true | Turn off to show the logo only without deleting the text. |
| use_logo | BOOLEAN | false | Master switch for drawing the logo. Source is either the panel upload (stored as logo_file above) or an IMAGE linked to the logo input - the link wins. |
| layout | COMBO | vertical | How logo and text stack when both are present: vertical (logo on top) or horizontal (logo on the left). |
| color | STRING | #FFFFFF | Text colour. Accepts #RRGGBB / #RGB / r,g,b / a colour name. |
| style | COMBO | soft | Style preset - one click swaps the whole stroke / shadow set: soft both on but subtle (default) readable on light and dark footage, without grabbing attention plain both off - cleanest, but may vanish on bright backgrounds outline solid stroke only - only needed when contrast swings hard outline_shadow both maxed - most legible, also most noticeable manual ignore presets, use the four controls below Note: presets override the stroke / shadow values below - pick manual to tune them yourself. |
| use_stroke | BOOLEAN | true | Stroke switch (only read when style=manual; presets take over otherwise). A stroke rings the glyphs and reads louder than the text itself on high-contrast shots - if the watermark feels heavy, lower stroke_opacity before turning it off. |
| stroke_width | INT | 30–24 | Stroke thickness in pixels. Only applies when the stroke is on. 2~4 is plenty. |
| stroke_color | STRING | #000000 | Stroke colour. |
| stroke_opacity | FLOAT | 0.550–1 | Opacity of the stroke itself - lower this first when the watermark feels heavy; keeps more legibility than switching it off. 1.0 = solid black outline (loudest); 0.5~0.6 = soft ring (default); 0 = same as off. |
| use_shadow | BOOLEAN | true | Shadow switch (only read when style=manual). A shadow thickens the silhouette without the hard edge a stroke leaves - ⚠ a dark shadow is invisible on night / black footage - use a stroke there, or lighten shadow_color. |
| shadow_color | STRING | #000000 | Shadow colour. Dark shadows disappear on dark footage; use a light colour like #FFFFFF. |
| shadow_opacity | FLOAT | 0.400–1 | Opacity of the shadow. 0.3~0.5 reads as 'visible outline, not in the way'. |
| shadow_offset | INT | 5-80–80 | Shadow offset in pixels (positive = down-right). 4~8 looks natural; 0 turns it into a symmetrical soft glow - lighter, and costs no margin in a corner. |
| shadow_blur | INT | 80–100 | Shadow blur radius in pixels. Bigger is softer. 6~14 is natural; 0 = a hard duplicate. |
| angle | FLOAT | 0.0-180–180 | Rotate the whole watermark. For tiled marks, -20~-30 degrees is much harder to align and erase. |
| font | COMBO | (built-in fallback font - CJK glyphs render as boxes) | Font. Sorted CJK-first on Windows (Microsoft YaHei / SimHei / SimSun / KaiTi ...). Fonts you drop into ComfyUI's input/fonts folder show up in this list too. Note: SimHei has no glyph for © - it renders as a box. Use Microsoft YaHei / SimSun / KaiTi for text containing ©. |
| font_file | STRING | Font file path relative to ComfyUI's input folder, e.g. fonts/MyFont.otf. When set and the file exists it overrides the font list above. Absolute paths and '..' are rejected. | |
| float_path | COMBO | diagonal | Path followed while floating: diagonal / horizontal / vertical constant back-and-forth (triangle wave) circle elliptical loop random uniform random jumps - strongest anti-theft, most jarring |
| float_cycles | FLOAT | 1.00.1–200 | How many round trips across the whole clip. 1 = one very slow pass; 4~8 = clearly wandering but still readable. For the random path this means 'number of position changes' - try 2x the clip length in seconds. A single still has nothing to travel across: the seed pins it to one spot. |
| seed | INT | 00–4294967295 | Used by the random float path only. Fix it to reproduce the same positions. |
| start_pct | FLOAT | 0.0000–1 | Where the watermark starts appearing, as a fraction of total length. 0 = from frame one. Only meaningful for multi-frame sequences: a single still counts as frame 0. |
| end_pct | FLOAT | 1.0000–1 | Where it stops, as a fraction of total length. 1 = stays to the end. e.g. 0.6 -> 1.0 shows it only in the last 40%. Only meaningful for multi-frame sequences: a single still counts as frame 0. |
| fade_frames | INT | 00–240 | Fade-in / fade-out length in frames. 0 = hard cut. Only meaningful for multi-frame sequences: a single still has nothing to fade. |
| tile_gap | INT | 900–1200 | Spacing between tiled watermarks, in pixels. |
| title_mode | COMBO | off | Head / tail black copyright card. off = do not add one. |
| title_seconds | FLOAT | 2.00.1–60 | Card duration in seconds. Frame rate comes from the input video. |
| title_text | STRING | Card text. Leave blank to reuse the watermark text above. | |
| title_bg_color | STRING | #000000 | Card background colour. |
| logo_file | STRING | Logo file name, relative to ComfyUI's input folder. Clicking 'Upload logo' in the visual panel fills this in; you normally never edit it by hand. You can also type the name of a file already placed in the input folder, e.g. logo/kaero.png. Note: a linked IMAGE on the logo input always wins over this. | |
| text_inopt | STRING | Optional. Feed the text in from another node (any node that builds a string). When connected and non-empty the wired text wins - whatever you typed in the panel is ignored. Disconnected, or an empty string upstream, falls back to the panel text. Typical use: while comparing parameter variants (resolutions / LoRA strengths / seeds), splice this run's parameters into the frame so you can tell at a glance which settings produced it. Note: it takes one plain string - nothing is filled in for you, so build the text you want upstream. | |
| logoopt | IMAGE | Optional. A PNG logo with an alpha channel. Only drawn when use_logo is on. Transparency comes from logo_mask first; falls back to the PNG's own alpha. | |
| logo_maskopt | MASK | Optional. Transparency mask for the logo; falls back to the PNG's own alpha. ComfyUI's Load Image outputs a PNG's transparency as a MASK - just wire it in. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | — |