Animated Mask Drawer 🎨
Draw a Mask That Moves With Your Video — Without Leaving ComfyUI
- video
- mask_sequence
- mask_preview
- masked_video
Here's the problem every video-inpainting workflow runs into: a static mask is useless on a moving subject. Draw a box around the thing you want to remove and by frame 30 it's slid halfway out of the frame. AnimatedMaskDrawer is the fix - a small roto editor that lives inside ComfyUI, lets you draw circle, rectangle, and bézier shapes over your actual video frames, and keyframes them automatically as you move them around the timeline.
Think of it as a stripped-down Nuke Roto or After Effects shape mask, bolted onto a node. You're not guessing coordinates; you're scrubbing frames and dragging shapes, the way you would in a real compositor. It's one of those "why did this take so long to exist" nodes.
How it works
The node stores your video frames per-node (keyed by the node's id, so multiple drawers don't clobber each other), then serves them to a full-screen web editor on ComfyUI's own server. You hit the 🎨 Launch Mask Editor button, a window opens, and you draw. Move a shape on a different frame and a keyframe is created; everything in between is linearly interpolated in the node's Python code when the prompt runs. Position/size/rotation keyframes show up blue in the timeline; opacity-only ones are orange, so you can tell fades from motion at a glance.
The keyframe data serializes into a hidden mask_data widget, which means your roto is baked into the workflow JSON - it survives restarts and travels with the .json when you share it. There's also a per-node disk backup under roto_data/ next to the node in case a workflow save goes sideways. If you already do roto in Nuke, it can import Nuke roto nodes directly via paste, which is a genuinely nice bridge for people who live in both tools.
The inputs and outputs that matter
video (IMAGE) is the only required input - feed it a video frame batch, which is just an IMAGE tensor where the first dimension is frames.
The optional ones are where the magic lives:
auto_resolution(default on) - matches the output mask to your video's resolution. Leave it on.width/height- only used when you turnauto_resolutionoff, for e.g. upscaled output masks. Leave at 0 otherwise.feather- Gaussian blur radius for soft edges; 10–20 is the sweet spot. Needsscipy(more below).invert- swap masked/unmasked areas, handy when you want to protect rather than replace.refresh- bump it to force the node to regenerate masks if an edit doesn't show up.mask_data- the hidden serialized shapes/keyframes; the editor writes it, you never touch it.
Three outputs: mask_sequence (MASK, one mask per frame - feed this to inpainting, effects, or compositing), mask_preview (your video with a red overlay, wire it to a Preview Image node to eyeball your work), and masked_video (mask burned in white at 100% opacity, for when you want to see the raw cut).
Installing it
The standard route - via ComfyUI Manager, search "Animated Mask Editor" - or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/BISAM20/ComfyUI-AnimatedMaskEditor.git
Then restart ComfyUI. It'll show up under Add Node → mask → animation → Animated Mask Drawer 🎨. Dependencies are light: torch, numpy, and Pillow already ship with ComfyUI. scipy is the one extra, and it's only used for feathering - without it, feather silently does nothing.
Gotchas
The workflow has a rhythm that trips people up at first. Queue your prompt once with the video connected so the frames reach the node - the editor says "⚠️ No video loaded" until you do. Then click Launch Mask Editor (allow pop-ups for ComfyUI or the window won't open), draw, hit 💾 Save & Return to ComfyUI, and queue again to generate the actual masks. Miss the first queue or the save, and you'll wonder why nothing changed.
The honest caveat: this is a new, small pack with next to no battle reports yet, so you're a little bit on your own. But the core loop is simple enough that the main failure modes are the ones above. For frame-accurate video masks - which instruction-editing models still can't deliver, since they can't keep unmasked pixels bit-identical across frames - this is the tool to reach for.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| video | IMAGE | Input video or image batch to create masks for | |
| mask_dataopt | STRING | Serialized roto shapes/keyframes (hidden - managed by the editor). Stored in the workflow so shapes survive restarts. | |
| widthopt | INT | 00–4096 | Manual output mask width. Only used when auto_resolution is off (0 = use video width). |
| heightopt | INT | 00–4096 | Manual output mask height. Only used when auto_resolution is off (0 = use video height). |
| featheropt | INT | 00–100 | Blur radius for soft edges (10-20 recommended for smooth masks) |
| invertopt | BOOLEAN | false | Invert mask (swap masked/unmasked areas) |
| auto_resolutionopt | BOOLEAN | true | Automatically match the mask output to the input video's resolution. Turn off to set width/height manually. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| mask_sequence | MASK | Animated mask sequence (batch) - Use for inpainting, effects, compositing |
| mask_preview | IMAGE | Video with red mask overlay - Connect to Preview Image to see masks |
| masked_video | IMAGE | Input video with white masks overlaid (100% opacity) |