kubakub sound mask swap
Make the layers trade places on the kick drum
- audio
- background
- masks
- pictures
- signal
- sound
- frames
- mask
- fps
- audio
- report
Wipe, iris, dissolve, region-by-region - most mask animation moves a mask across a picture. This node does the opposite: the pictures stay where they are on the facade and the masks move. One layer's window becomes another layer's window, on the beat, and because the masks belong to real regions of a building, the swap reads as the building rearranging itself rather than as a filter.
It's the standalone version of a behaviour that also lives inside the kubakub director ("swap masks"), and it's the one to reach for when you want the effect as frames in a normal ComfyUI video graph.
How it's set up
Each layer has a mask - and optionally a picture that shows through it. You connect masks as a mask batch (at least two) and pictures as an image batch, one whole picture per layer, repeated if there are fewer pictures than masks. The pictures don't move; only which mask each layer currently holds. Empty everything and it runs on a sample facade with the windows as one layer per floor and a built-in 120 bpm beat, which is a genuinely good way to see what the node does before you commit inputs.
The timing inputs
step_on is the heart of it:
beatsorbars- from the tempo of the sound. nth steps on every second, third, fourth beat.every- a fixed number of seconds between steps.low,mid,high- step on a hit in those frequency bands. Kick and bass, mids, hats and clicks. threshold (default 0.3) is how strong a hit must be to count, and gap (default 0.1 s) sets the shortest time between two accepted hits so a drum roll doesn't flicker the masks into mush.signal- step each time a control track on the signal audio input rises throughthreshold. That's a wav - a gate track, a trigger track, an LFO, an envelope, CV recorded off a synth. Its time base follows the sound's.list- the exact times in seconds, comma or space separated, in step_times. The manual mode, and the one to use when the show is cut to a click track.
bpm at 0 means "find it in the sound"; type a number to override. start offsets seconds into the sound. fps and seconds define the render - and seconds at 0 means "to the end of the sound", which is usually what you want.
order decides what the masks do: loop moves every layer on to the next mask; pingpong goes there and back; random reshuffles each step, with seed picking the shuffle.
fade and fade_style are the polish. At 0 the layer simply appears in its new mask. Above 0, crossfade fades the new picture in over the old one, while out, then in takes each layer out of its old mask first and then into its new one - a paler, more deliberate effect, close to a stage light change. feather softens the mask edges in pixels of the background.
scale (default 0.5) is the one input you should not ignore. Frames are generated at this fraction of the background's size, and the author puts the reason in the tooltip: 250 frames at 1600×1080 is about 7 GB of RAM. Half scale gets you to a quarter of that.
mask_of picks which layer's moving mask comes out on the mask output.
Outputs
frames - background plus every picture in whatever mask its layer currently holds. mask - the moving mask of the chosen layer, one per frame, which is the output that makes this composable: feed it to a glow, a cut-out, a light layer, or anything that takes a mask. fps for Create Video, audio cut to the length of the frames, and report telling you the tempo or hits it found, how many steps, frames and the size.
Install
cd ComfyUI/custom_nodes && git clone https://github.com/kubakubkub/ComfyUI_KubaNodes
python -m pip install -r ComfyUI_KubaNodes/requirements.txt
Part of the 2d / motion group. Nothing else is needed here - no model, no Blender, no ffmpeg unless you're exporting the result. If the node doesn't appear in your menu, it's likely the 2d/motion group is switched off in kubakub.ini; the console says what was hidden at startup.
What goes wrong
Tempo detection is the usual candidate. If your track has a weak or ambiguous pulse, beats will find something, but not necessarily what you're counting - check the report's tempo, and if it's wrong, nth plus a typed bpm beats fighting the detector. For anything with a strong four-on-the-floor, low with threshold around 0.3 is more musical than beats anyway, because it follows the actual kick rather than a grid.
And a design note worth internalising: this is a deterministic effect. The masks swap; nothing is generated. Which means it's instant, reproducible, and completely indifferent to what your pictures actually are. Punch in a set of photos of the building at different times of day and you get a time-lapse-by-drum-machine, which is a thing projection shows do constantly and which no diffusion model would give you reliably.
Inputs (23)
| Name | Type | Default | Description |
|---|---|---|---|
| order | COMBO | loop | loop: every layer moves on to the next mask. pingpong: there and back. random: a new shuffle every step. |
| step_on | COMBO | beats | What makes a step: beats or bars (tempo of the sound), every (a fixed time), a hit in the low (kick, bass), mid or high (hats, clicks) frequencies, signal (the control wav in 'signal' rises through the threshold) or list (the times in 'step_times'). |
| nth | INT | 11–64 | Step on every nth beat, bar or hit. |
| fade | FLOAT | 0.000–10 | Seconds of fade into the new mask. 0 = the layer just appears there. |
| fade_style | COMBO | crossfade | With a fade: crossfade = the new picture fades in over the old one. out, then in = each layer fades out of its old mask first, then into the new one. |
| feather | FLOAT | 00–500 | Soft mask edges, in pixels of the background. 0 = the masks as they are. |
| threshold | FLOAT | 0.300–1 | low / mid / high: how strong a hit has to be to count (0 = every hit, 1 = only the strongest). signal: the level it has to rise through (0-1 of its peak). |
| fps | FLOAT | 25.001–120 | Frames per second. |
| seconds | FLOAT | 0.00–3600 | Length to render. 0 = to the end of the sound. |
| scale | FLOAT | 0.500.05–1 | Size of the frames relative to the background (RAM: 250 frames at 1600x1080 = ~7 GB). |
| mask_of | INT | 11–64 | Which layer's moving mask goes out on 'mask' (1 = the first mask of the batch). |
| audioopt | AUDIO | The sound the masks react to. Empty = a built-in beat (120 bpm). | |
| backgroundopt | IMAGE | The picture behind the layers; it gives the size. Empty = the sample facade. | |
| masksopt | MASK | One mask per layer (a mask batch, at least two). Empty = the windows of the sample facade, one layer per floor. | |
| picturesopt | IMAGE | One whole picture per layer (an image batch, repeated when there are fewer than masks). Each shows through the mask its layer holds at that moment. Empty = a flat colour per layer, to see the masks move. | |
| signalopt | AUDIO | step_on = signal: a control track as a wav (a gate or trigger track, an LFO, an envelope, CV recorded from a synth). A step each time it rises through 'threshold'. Its time runs with the sound's. | |
| step_timesopt | STRING | step_on = list: the times of the steps in seconds, separated by commas, spaces or new lines (e.g. 0.5, 1, 1.75, 3). | |
| startopt | FLOAT | 0.00–3600 | Seconds into the sound where the frames start. |
| bpmopt | FLOAT | 0.00–400 | Tempo for beats / bars. 0 = found in the sound. |
| everyopt | FLOAT | 0.500.02–600 | step_on = every: seconds between two steps. |
| gapopt | FLOAT | 0.100–10 | low / mid / high: the shortest time between two steps, so a roll does not flicker. |
| seedopt | INT | 10–2147483647 | order = random: another shuffle. |
| 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 |
|---|---|---|
| frames | IMAGE | Every frame: the background with each picture (or colour) in the mask its layer holds. |
| mask | MASK | The moving mask of the layer chosen in mask_of, one per frame. |
| fps | FLOAT | Frames per second. |
| audio | AUDIO | The sound, cut to the frames. |
| report | STRING | Tempo or hits found, the number of steps, frames and size. |