Nodes/ComfyUI VideoMark(视频水印)/VideoMark Overlay(画面水印)
ComfyUI Node

VideoMark Overlay(画面水印)

Stamp your name on the frames, whatever generated them

By kaerozhi·Created 3 days ago·Updated 2 days ago· 2
VideoMark Overlay(画面水印)
  • images
  • logo
  • logo_mask
  • images
modecorner
positionbottom_right
text© 2026 kaero
opacity0.85
scale20.0
margin32
use_texttrue
use_logofalse
layoutvertical
color#FFFFFF
stylesoft
use_stroketrue
stroke_width3
stroke_color#000000
stroke_opacity0.55
use_shadowtrue
shadow_color#000000
shadow_opacity0.40
shadow_offset5
shadow_blur8
angle0.0
font(内置兜底字体·中文会显示为方块)
font_file
float_pathdiagonal
float_cycles1.0
seed0
start_pct0.000
end_pct1.000
fade_frames0
tile_gap90
logo_file
text_in

A watermark node that has never heard of Wan

Every local video pipeline narrows to the same shape at the end: the model decodes to an IMAGE batch - Wan 2.2, LTX-2, MiniMax H3, Hunyuan, AnimateDiff, indistinguishable from here - and then something turns that batch back into a file. VideoMark Overlay sits in that gap. IMAGE batch in, your text and/or a PNG logo painted onto every frame, IMAGE batch out. No model load, no audio, no file I/O, no idea what generated the frames.

That sounds dull and it's the point. ComfyUI's post-processing options tend to be either "throw another diffusion pass at it" or nothing at all, and a watermark is neither - it's a deterministic pixel op, the kind of job a compositor does perfectly and a sampler only ruins. It also skips the export-review-reimport loop, and single images work unchanged: one photo is a batch of length 1, laid out from its own width and height.

Fair warning: this pack is obscure. Searching the r/StableDiffusion + r/comfyui + r/civitai corpus for "videomark" across two years returns nothing, so there's no adoption story to point you at - and the watermark talk that does exist mostly runs the other way, people removing marks rather than adding them.

How it actually works

The mechanism lives in render.py and it's the right one: at the start of a run the node renders a single RGBA "stamp" at the size of your frames - text, optional logo, stroke and shadow already baked in - then alpha-composites that one sprite onto each frame. Cost per frame is flat, so a 240-frame clip doesn't pay for 240 text renders. It's numpy + Pillow underneath, not torch math: no VRAM cost, nothing to download. Frames are copied before compositing, so upstream tensors aren't mutated.

The number that matters most is scale, the stamp's width as a percentage of frame width rather than pixels. That's why settings tuned at 480p survive a jump to 1440p.

The inputs you'll actually touch

  • mode - corner (the default; which corner via position), center, floating, tile. Corner is the answer most of the time: bottom_right unless a platform's like/share buttons live there, in which case top_left.
  • text, scale (20% is a sane start for text, 8–18 for a logo), margin (distance from the edge, in pixels - 24–40 for 736×992, 36–64 for 1080p), opacity (0.85 at a corner, and drop to ~0.15 if you go center).
  • style - a one-click preset: soft (default), plain, outline, outline_shadow, manual. While a preset is selected the four stroke/shadow controls are ignored entirely, so switch to manual before hand-tuning.
  • use_logo is off by default, so a connected logo does nothing until you flip it. The image comes from the optional logo IMAGE input (a wire beats the file) or from logo_file, the input-folder filename the panel's "Upload Logo" button fills in for you.

Optional text_in takes a STRING from any upstream node - Text Concatenate, String Function - and connected, non-empty text overrides the widget. Blank upstream falls back to the widget rather than silently deleting your watermark, which is the sane behaviour when the upstream node returns "".

One output, images: SaveImage for a still, or your video-combine node (VHS Video Combine, Create Video → Save Video) for frames.

Install

# ComfyUI Manager: search "comfyui-videomark"
cd ComfyUI/custom_nodes
git clone https://github.com/kaerozhi/comfyui-videomark.git

Registry route: comfy node install comfyui-videomark. Either way there's no pip step - the pack declares zero dependencies and rides on the torch/numpy/Pillow ComfyUI already ships, which is rare enough in a node pack to be worth saying out loud. Restart afterwards: the visual panel lives in web/, and that directory is only scanned at startup.

Where people get burned

Fonts. 黑体 (simhei) has no © glyph, so a copyright notice renders as boxes; use 微软雅黑 / 宋体 / 楷体, or point font_file at an absolute path. If the dropdown shows only the built-in fallback, font_file is your only route to non-box characters.

Stroke eats your width budget. scale measures the stamp including the stroke, so cranking stroke_width makes the letters smaller. If the mark feels too heavy, lower stroke_opacity first - 1.0 is a solid black ring, 0.5–0.6 a soft edge, 0 is off.

Dark shadow on dark footage is invisible. A black drop shadow on a night shot contributes nothing. Keep the stroke, or set shadow_color light. shadow_offset: 0 turns the shadow into a symmetric halo - lighter to look at, and it doesn't eat into your margin.

On a single photo, start_pct, end_pct, fade_frames and float_cycles do nothing: there's no timeline to walk and no previous frame to fade from. The photo is frame 0, which the default 0→1 window already covers.

Batching mixed photos isn't this node's job. IMAGE is a same-size tensor; 1200×800 and 800×1200 can't share a batch.

Failures pass through silently. If rendering throws, you get your frames back unchanged and one [VideoMark] line in the console. No error - just an unmarked video. The panel also has no live preview by design; you see the result by rendering one.

Editing anything under web/ needs a ComfyUI restart. A JS-only tweak gets away with Ctrl+Shift+R.

CategoryVideoMark

Inputs (35)

NameTypeDefaultDescription
imagesIMAGE
modeCOMBOcorner水印方式: corner 固定贴四角之一(最不挡画面,日常首选) center 画面正中,建议 opacity 压到 0.15~0.25 floating 在安全区内游走 / 周期跳位(防盗最强,观感最差) tile 全画面平铺,最难裁掉抹除
positionCOMBObottom_rightcorner 模式贴哪个角。 右下角最不挡主体;若右下角有平台 UI 遮挡(点赞/分享按钮),改用左上角。
textSTRING© 2026 kaero水印文字,支持多行(直接回车换行)。
opacityFLOAT0.850.02–1不透明度。参考值: 四角固定 0.75~1.0(要看得清) 居中 / 平铺 0.12~0.22(再高就影响观赏) 浮动 0.25~0.45(太低会被压暗后抹掉)
scaleFLOAT20.01–100水印宽度占画面宽度的百分比。换分辨率不用改,会自动等比换算。 文字水印 15~25 比较舒服;logo 水印 8~18。
marginINT320–600距画面边缘的安全边距(像素)。浮动 / 平铺同样受它约束,图章不会被切掉。 参考:736×992 用 24~40;1080p 用 36~64。
use_textBOOLEANtrue关掉可临时只留 logo,不必删掉文字内容。
use_logoBOOLEANfalse打开才会绘制 logo。 图片来源二选一:面板里「上传 Logo」(存成上面的 logo_file), 或者把 LoadImage 的 IMAGE 接到 logo 输入口(接线优先)。
layoutCOMBOverticallogo 与文字同时存在时的排布:vertical 上下排(logo 在上),horizontal 左右排(logo 在左)。
colorSTRING#FFFFFF文字颜色。支持 #RRGGBB / #RGB / r,g,b / 英文颜色名。
styleCOMBOsoft水印样式预设 —— 一键换整套描边 / 投影参数: soft 描边 + 投影,两者都淡(默认) 明暗背景都读得清,又不像实心描边那样抢画面 plain 两个都关 —— 最不干扰画面,但亮背景上可能糊掉 outline 只留实心描边 —— 画面明暗变化剧烈时才需要 outline_shadow 描边 + 投影都拉满 —— 最清楚,同时也最显眼 manual 不用预设,完全按下面四个控件的数值来 注意:选预设时下面的描边/投影数值会被预设覆盖,要自己调就选 manual。
use_strokeBOOLEANtrue描边开关(仅 style=manual 生效,其余预设会接管)。 描边是沿文字外圈描的一圈边,在明暗变化大的镜头上比字本身还显眼 —— 觉得水印太重,优先降 stroke_opacity,而不是直接关掉。
stroke_widthINT30–24描边粗细(像素)。只在描边打开时生效。画面明暗变化大时给 2~4 就够。
stroke_colorSTRING#000000描边颜色。
stroke_opacityFLOAT0.550–1描边自身的不透明度 —— 觉得描边太重就先降这个,比直接关掉更保留可读性。 1.0 = 实心黑边(最显眼);0.5~0.6 = 柔和一圈(默认);0 = 等于关掉描边。
use_shadowBOOLEANtrue投影开关(仅 style=manual 生效)。 投影把轮廓往外晕开一点:读得清,却不像描边那样在画面上糊一圈硬边。 ⚠ 深色投影在夜景 / 黑幕上是隐形的,纯暗调画面请靠描边(或把 shadow_color 调亮)。
shadow_colorSTRING#000000投影颜色。纯暗调画面里深色投影看不见,可改成 #FFFFFF 之类亮色。
shadow_opacityFLOAT0.400–1投影自身的不透明度。0.3~0.5 是「看得见轮廓但不抢眼」的甜区。
shadow_offsetINT5-80–80投影偏移(像素,向右下为正)。4~8 像自然投影; 0 则变成四周对称的柔和光晕,观感更轻,贴角时也不占边距。
shadow_blurINT80–100投影模糊半径(像素)。越大越柔。6~14 自然;0 = 硬边(等于把字复制一份)。
angleFLOAT0.0-180–180水印整体旋转角度。平铺时配 -20~-30 度最难对齐抹除。
fontCOMBO(内置兜底字体·中文会显示为方块)中文字体。列表按「中文优先」排序(微软雅黑 / 黑体 / 宋体 / 楷体…)。 想用列表外的字体(手写体、商用字体),把下面 font_file 填成绝对路径即可,它会覆盖这里。 注意:黑体(simhei)没有 © 字形,用它会显示成方框 —— 含 © 的文字请用微软雅黑 / 宋体 / 楷体。
font_fileSTRING自定义字体文件绝对路径,例:D:/Fonts/思源黑体.otf 填写且文件存在时,忽略上面的字体下拉框。
float_pathCOMBOdiagonalfloating 模式的活动轨迹: diagonal / horizontal / vertical 匀速来回(三角波,端点不减速) circle 椭圆环路 random 均匀随机跳位,防盗最强但位置会突变
float_cyclesFLOAT1.00.1–200floating 模式:整段视频内来回走完的圈数。 1 = 整段走一个来回(很慢,适合长视频);4~8 = 明显游走但仍看得清。 random 轨迹下含义变为「位置切换次数」,建议设成 2×视频秒数(约每 0.5 秒跳一次)。 单张图片上没有「走」这回事:位置由 seed 定死成一个点,每次跑都一样。
seedINT00–4294967295仅 random 轨迹使用,固定后每次生成的位置一致,便于复现。
start_pctFLOAT0.0000–1水印出现的起始位置(占视频总长的比例)。0 = 片头就出现。 只对多帧序列有意义:单张图片整张算第 0 帧,这一项不影响它。
end_pctFLOAT1.0000–1水印消失的位置(占总长比例)。1 = 一直留到片尾。 例:0.6 → 1.0 表示只在后 40% 出现。 只对多帧序列有意义:单张图片整张算第 0 帧,这一项不影响它。
fade_framesINT00–240出现 / 消失时的淡入淡出帧数,0 = 硬切。 只对多帧序列有意义:单张图片没有淡入淡出可做,这一项不影响它。
tile_gapINT900–1200tile 模式下水印之间的间距(像素)。
logo_fileSTRINGlogo 文件名(相对 ComfyUI 的 input 目录)。 在可视化面板里点「上传 Logo」会自动填这一项,通常不用手改; 也可以手动填一个已经放进 input 目录的文件,例:logo/kaero.png 注意:若 logo 输入口接了线,以连线的图为准,这里会被忽略。
text_inoptSTRING可选。从外部接入文字(接上游拼字符串的节点,例如 String Function)。 接了线且内容非空 → **以连线为准**,面板里手填的文字自动让位; 断线、或上游传空字符串 → 回落到面板手填的文字。 典型用法:跑参数对比(侧视图 / 不同分辨率 / 不同 LoRA 强度 / 不同 seed)时, 把这一轮的参数拼成文字烧进画面,回看结果时一眼就知道是哪一套。 注意:它只吃「一段字符串」,节点不会替你填任何值 —— 要显示什么就在上游拼什么。
logooptIMAGE可选。带 alpha 通道的 PNG logo。接上后把 use_logo 打开才会生效。 透明度优先取 logo_mask;没接 mask 就用图片自带的 alpha 通道。
logo_maskoptMASK可选。logo 的透明度遮罩,留空则用 logo 图片自带 alpha。 ComfyUI 的「加载图像」会把 PNG 透明通道单独输出成 MASK,直接接过来即可。

Outputs (1)

NameTypeDescription
imagesIMAGE