kubakub mask animate
Masks that move, on a curve, on the beat
- mask
- field
- audio
- sound
- masks
- fps
- audio
- frame_count
- report
What it's for
This is the node that makes a static building move. It outputs one mask per frame - a whole clip's worth - so a wipe travels across the facade, a band runs along a wall, an iris opens on a window, or the mask itself drifts, turns, scales or strobes.
Its natural partner is kubakub mask field, which supplies the grey ramp the move travels along. Mask field decides where; mask animate decides when and how. Chain two of them (masks output into the next node's mask input) and you get two moves at once - a wipe and a scale, a band and a strobe.
And it doesn't need a diffusion model to be useful: feed the masks into MaskToImage → Create Video and you've generated a projection-mapped animation with no AI in the loop at all. For a working VJ that's arguably the most valuable thing in the pack.
How it works
Two dropdowns do everything, and separating them is the whole design.
effect is what moves. Along the field: reveal (black to white), hide, band (a stripe travelling through), rings (stripes that keep coming). The mask itself: move x, move y (in picture widths/heights), rotate (in turns), scale (1 = as-is), opacity (0 to 1).
curve is how the value runs over time. ramp runs once and stays. saw runs again and again. triangle goes there and back. sine is the soft version of that. square is on/off - with opacity that's a strobe, with duty controlling how long the on part lasts. random picks a new value every cycle, noise wanders. Then the sound curves: sound level follows loudness, and beats / bars / low / mid / high hits each start a fresh ramp when one lands.
That last group is the party trick. Sound curves work with no audio connected - they fall back to a built-in 120 bpm beat - and with audio you get tempo detection, nth to use only every second or fourth hit, threshold and gap for detecting actual hits rather than rumble, and listen to pick which frequency band counts (kick, hats). steps is the one worth playing with: at 1, every beat replays the same move; at more than 1, each beat advances by that many steps, which is how you get one more region lighting up per beat when the field is a region order ramp.
The curve is literally drawn on the node - buttons set the type, dragging left/right on the curve changes the speed. Small thing, but it means you can dial a tempo without staring at numbers.
Inputs and outputs
Set these first: effect, curve, cycle (seconds per cycle - or for beats and hits, seconds the ramp takes after each one), value_from / value_to (the bottom and top of the curve, in whatever units the effect uses), easing, and width/rings/soft for the band and ring effects.
Then the ones you'll come back to: trail (seconds a place keeps glowing after the mask has moved past - 0 by default, and it's the difference between a wipe and a comet), fps, seconds (0 means take the length from the sound, else the mask batch, else 4 seconds), and scale (0.05 to 1, mask resolution relative to the input - at 250 masks of 3840×2160 a full-size batch is around 8 GB of RAM, so scale down while you're iterating).
Optional inputs: mask (what's being animated - for the field effects it limits the result), field (the ramp; empty means a plain left-to-right ramp over the mask), audio, plus phase, duty, seed, listen, gain, release, nth, steps, threshold, gap, bpm, start, pivot and wrap for the specific curve and effect you picked.
Outputs: masks (one per frame), fps, audio (the sound, cut to the frames - silence if none, so it's always safe to wire into Create Video), frame_count, and report (frames, size, the curve's range, and whatever tempo or hits it found). That fps/audio pair is what makes the output directly video-ready.
Install
Manager → search kubakub nodes → install → restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/kubakubkub/ComfyUI_KubaNodes
python -m pip install -r ComfyUI_KubaNodes/requirements.txt
ComfyUI 0.37.0+; opencv-python-headless is the only added package (keep an existing OpenCV build). Both mask nodes landed in 0.3.0 and added no new dependencies. There's a model-free example workflow - example_workflows/mask_animate.json - that runs with nothing connected.
Common issues
- RAM explosion. 250 frames of 4K float masks is about 8 GB. Drop
scaleto 0.5 while you tune, and setsecondsto something honest. - The sound curves do nothing useful. Check
bpm(0 = detect, and detection is only as good as the track),thresholdfor hits, andlistenso your kick isn't being masked by hi-hats. - Nothing connected, still got masks. By design - it runs a sample. Great for learning, confusing the first time.
- Two moves at once. Chain two nodes: the first node's
masksinto the second'smask. It's the documented approach, not a hack.
Inputs (31)
| Name | Type | Default | Description |
|---|---|---|---|
| effect | COMBO | reveal | Along the field: reveal (appears from black to white), hide, band (a stripe travels through), rings (stripes that keep running). The mask itself: move x / move y (value = picture widths / heights), rotate (value = turns), scale (value = size, 1 = as it is), opacity (value 0 to 1; with a square curve = strobe). |
| curve | COMBO | ramp | How the value runs. ramp: once, then it stays. saw: again and again. triangle: there and back. sine: a soft there and back. square: on / off. random: a new value every cycle. noise: wanders. sound level: follows the loudness. beats / bars / low / mid / high hits: each one starts a ramp. |
| cycle | FLOAT | 2.000.01–3600 | Speed: seconds for one cycle of the curve (for beats and hits: seconds the ramp takes after each one). |
| value_from | FLOAT | 0.00-100–100 | The value at the bottom of the curve. reveal / band / rings / opacity: 0 to 1. move: picture widths or heights (-0.5 = half a picture to the left / up). rotate: turns. scale: size. |
| value_to | FLOAT | 1.00-100–100 | The value at the top of the curve. |
| easing | COMBO | linear | ramp, saw, triangle, beats and hits: how the ramp starts and stops. |
| soft | FLOAT | 0.050–1 | reveal / hide / band / rings: how soft the moving edge is (0 = hard, 1 = the whole field fades at once). |
| width | FLOAT | 0.200.01–1 | band / rings: how wide a stripe is (1 = the whole field). |
| rings | INT | 31–64 | rings: how many stripes. |
| trail | FLOAT | 0.000–60 | Seconds a place keeps glowing after the mask has moved on. 0 = no trail. |
| fps | FLOAT | 25.001–120 | Frames per second. |
| seconds | FLOAT | 0.00–3600 | Length. 0 = the length of the sound, else of the mask batch, else 4 s. |
| scale | FLOAT | 1.000.05–1 | Size of the masks relative to the input (RAM: 250 masks at 3840x2160 = 8 GB). |
| maskopt | MASK | What is animated: a mask, or a mask batch (one per frame, e.g. from another kubakub mask animate). For reveal / hide / band / rings it limits the result. | |
| fieldopt | MASK | For reveal / hide / band / rings: the grey ramp from kubakub mask field. Empty = a left to right ramp over the mask. | |
| audioopt | AUDIO | For the sound curves, and passed on to 'audio' for Create Video. A sound curve without a sound runs on a built-in beat (120 bpm). | |
| phaseopt | FLOAT | 0.000–1 | Where in its cycle the curve starts (0.5 = half a cycle later). |
| dutyopt | FLOAT | 0.500.01–0.99 | square: the part of the cycle it is on (0.1 = short flashes). |
| seedopt | INT | 10–2147483647 | random / noise: another run. |
| listenopt | COMBO | everything | sound level: the whole sound, or only its low (kick, bass), mid or high (hats, clicks) frequencies. |
| gainopt | FLOAT | 1.000–10 | sound level: how strongly the loudness drives the value. |
| releaseopt | FLOAT | 0.150–5 | sound level: seconds the value takes to fall after a peak. |
| nthopt | INT | 11–64 | beats / bars / hits: only every nth one counts. |
| stepsopt | INT | 11–512 | beats / bars / hits: 1 = each one runs the whole move again. More = each one moves on by one step of that many (e.g. the number of regions with a 'region order' field: one more region per beat). |
| thresholdopt | FLOAT | 0.300–1 | low / mid / high hits: how strong a hit has to be to count. |
| gapopt | FLOAT | 0.100–10 | low / mid / high hits: the shortest time between two. |
| bpmopt | FLOAT | 0.00–400 | beats / bars: a tempo you know. 0 = found in the sound. |
| startopt | FLOAT | 0.00–3600 | Seconds into the sound where the frames start. |
| pivotopt | COMBO | mask centre | rotate / scale: around the mask's own centre or the middle of the picture. |
| wrapopt | BOOLEAN | false | move: what leaves on one side comes back in on the other. |
| soundopt | KUBA_SOUND | From kubakub sound: the sound and how it is heard (tempo, gain, release, band, threshold, gap), set once for every node that listens, so all of them step on the same beat. Connected, it comes before 'audio' and before this node's own sound settings. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| masks | MASK | One mask per frame. |
| fps | FLOAT | Frames per second. |
| audio | AUDIO | The sound, cut to the frames (silence without one). |
| frame_count | INT | How many frames. |
| report | STRING | Frames, size, the range of the curve, tempo or hits found. |