TimeSlice Wave
Liquid ripple without a physics sim
- images
- mask
- images
- mask
- num_frames
- slice_edges
The pitch: same slicing engine as the pack's TimeSlice Effect, but instead of a fixed cascade, each slice's time offset follows a sine wave that travels across the frame as the clip plays. The result is an undulating, liquid ripple - parts of the image lag behind, then catch up, like watching footage through a wavy pane of glass.
Why you'd reach for it: Effect gives you art-directed smears; Wave gives you motion that breathes. It's the one to grab when you want an organic pulse instead of a mechanical streak - a moving background that feels alive without ever looking like a filter. And because it's deterministic (same seed, same settings, identical output every run), it's the rare effect that's genuinely loopable to the frame.
How it works. Wave shapes its displacement with two frequencies. wave_count is the spatial frequency - how many full sine cycles sit across the slices, i.e. how many ripples you can see at once. wave_speed is the temporal frequency - how many full cycles the wave travels over the whole clip. Each slice's offset is max_offset × sin(2π × (wave_count × slice + wave_speed × frame)). You don't need the formula; you need the two knobs it explains.
The inputs that matter:
- images - a clip (batch of frames). Same rule as the rest of the pack: one frame does nothing.
- num_slices - band count (30 default).
- max_offset - peak frame displacement at the wave crest. This is your intensity slider.
- wave_count - ripples across the frame. 1–2 is tasteful; 10+ drifts into "broken signal" territory.
- wave_speed - how fast the ripple travels. Here's the one that matters: whole-number values loop seamlessly with
loop_mode: loop. That's the entire trick to a perfect liquid loop. - angle, loop_mode, blend_width, spacing, seed, mask - the pack-standard shared controls;
blend_width20–60 is where the liquid look actually lives.
Outputs. images, mask, num_frames, slice_edges - identical sockets to the whole pack. slice_edges is extra fun here: the wave's travelling boundary lines make a lovely moiré mask for a glow overlay.
Install
Same as the rest of the pack - ComfyUI Manager, search "TimeSlice Nodes", Install, restart. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/systms-ai/TimeSlice-Nodes.git
Restart and it appears under Systms → effects. Only dependency is numpy (torch comes from the ComfyUI runtime). No models, no checkpoints, nothing to download.
The gotchas
trim is the trap. Because the wave swings both backwards and forwards in time, trim has to drop max_offset frames from both ends - the output is input − 2 × max_offset. Crank max_offset to 20 on a short clip and you lose 40 frames of it. That's exactly what the num_frames output is for; wire it to your saver so it knows the real length.
The other one: at high wave_count the ripple starts to alias against the slice geometry and reads as noise rather than smooth liquid. If it looks like static, lower wave_count or raise num_slices instead of fighting it with blending.
Recipe to start: num_slices 60, max_offset 8, wave_count 1, wave_speed a whole number like 2, loop_mode loop, blend_width 40. That cycles perfectly, forever - genuinely satisfying as an idle background loop.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | Source video frames to slice. | |
| num_slices | INT | 301–2000 | Number of slices to divide the frame into |
| max_offset | INT | 81–120 | Peak frame offset at the wave crest |
| wave_speed | FLOAT | 2.000–50 | Full wave cycles over the clip. Higher = faster. Whole numbers loop seamlessly with loop_mode 'loop'. |
| wave_count | FLOAT | 1.000.25–20 | Number of full sine cycles across all slices |
| angle | FLOAT | 0-180–180 | Slice angle in degrees. 0 = vertical, 90 = horizontal. |
| loop_mode | COMBO | How out-of-range frame indices are handled: wrap around (loop), bounce back (ping_pong), or shorten the clip (trim). | |
| blend_widthopt | INT | 00–400 | Soft blend width (px) across boundaries. 0 = hard edges. |
| spacingopt | COMBO | How slice widths are distributed across the frame: even (linear), eased toward one side, or random. | |
| seedopt | INT | 00–4294967295 | Seed for 'random' spacing |
| maskopt | MASK | Optional mask. Slicing is applied only inside the white area; outside keeps the original frame. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| mask | MASK | — |
| num_frames | INT | — |
| slice_edges | MASK | — |