😺NKD Timeline
An actual video editor inside ComfyUI
- media
- images
- mask
- coverage
- generate
- audio
- audio_mask
- audio_ranges
- width
- height
- fps
- frame_count
- duration
- current_frame
- current_image
- markers
- raw_images
You've been hand-counting frames
Video workflows in ComfyUI are doable, but the "edit" part is where the graph falls apart. You compute trim ranges by hand, feed frame_count = 97 into an empty latent, and pray three clips line up. NKD Timeline is the antidote: a real multi-track timeline inside a node, where you drag clips, trim, slip, blade and layer them, and get the numbers back as sockets the rest of the graph actually consumes. It ships in NKD Preview Tools by Nekodificador, a ComfyUI author with a real reputation for "the editor should just work" tooling - his NKD VFX and Klein Tools packs pull hundreds of upvotes on r/comfyui, and people describe using his nodes daily.
What actually happens under the hood
The node is split-brain. A frontend editor (scrub, trim, Alt-slip, blend modes, markers) serializes your cut into a hidden timeline widget, and a Python backend parses that JSON and does the real work: decoding each source in windows, compositing frames in track order with blend modes, mixing the audio lanes through PyAV, and handing back one tidy image batch plus a set of masks. The media input is an autogrow socket - connect a video, an image sequence, a mask or audio to media_0 and another slot appears. The editor figures out which lane each thing belongs on.
The inputs that matter
model- the gotcha beginners miss. Pick the model this timeline feeds and the frame count snaps to the grid that model requires: Wan and Hunyuan want 4n+1, LTX and Cosmos 8n+1, Mochi 6n+1, MiniMax H3 17n+5. If a clip's rate doesn't match the timeline fps, the editor flags it rather than resampling it silently.import_mode-stacklayers each source on its own track from frame 0 (higher track wins; set blend modes via right-click);appendassembles a sequence on one track.width/height- 0 means "take the first clip's resolution." Or pick an aspect ratio and amegapixelsbudget and let it compute, rounded tosize_multiple.fps,start_frame,frame_count- the classic trio.frame_count0 runs to the end of the last clip.
Wire the outputs
images feeds your VAE encode and sampler. The mask outputs are where this node gets clever: coverage is WHITE where the timeline is empty - the stretches the editor labels "generate" - and generate is the mask lane UNION those gaps. Feed generate straight into a temporal inpainting mask and the model fills exactly the holes you left, no manual inversion. Then width, height, fps and frame_count drive your empty latent so the render matches the cut. current_image hands back the composited frame under the playhead, and markers feeds NKD Freeze Frames, its sibling in this pack.
Install and run
No model downloads, no dependency hell - the pack declares zero Python deps, and encoding rides on PyAV, which is already a hard dependency of core ComfyUI. Install via Manager (search "NKD Preview Tools") or:
cd ComfyUI/custom_nodes
git clone https://github.com/Nekodificador/ComfyUI-NKD-Preview-Tools
Restart, drop the node in under 😺NKD Nodes/Preview, connect a Load Video, hit conform to take fps and resolution from the first clip, and scrub the ruler. Space plays, J/K/L shuttles, I/O set the in and out points, Ctrl+wheel zooms, M drops a marker, Q and E bring a clip's edges to the playhead.
Where people trip
Run it with nothing connected and you get "the timeline is empty" - wire a source. If a workflow saved before mid-2026 comes up miswired, the author deliberately rebuilt the widget and output order once (a one-time break for a small audience), so the node may need re-adding. And check clip_audio: your videos' own sound only rides along if that's on.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| media | COMFY_AUTOGROW_V3 | Connect a video, an image sequence, a mask or audio - the same socket takes any of them and the timeline puts it on the right lane. More slots appear as you connect. | |
| timeline | STRING | — | |
| import_mode | COMBO | stack | Where a newly connected source is placed. 'stack' gives each one its own track from frame 0, so they layer like any other timeline - the higher track is the one you see. 'append' puts it after the previous one on the same track, to assemble a sequence. |
| aspect_ratio | COMBO | Custom | 'Custom' uses width/height below, which is what this node always did. Any other ratio computes them from the megapixel budget, and the monitor follows immediately - no run needed. |
| width | INT | 00–16384 | 0 = take the width of the first clip. |
| height | INT | 00–16384 | 0 = take the height of the first clip. |
| megapixels | FLOAT | 1.000.05–16 | Pixel budget for the chosen ratio. Ignored when aspect_ratio is 'Custom'. |
| size_multiple | INT | 161–64 | Round the computed size to a multiple of this. Match the model's canvas grid (MiniMax H3 uses 32) or it resizes every frame again on its way in. |
| fit | COMBO | contain | How a clip is fitted when its aspect ratio does not match the output. The preview shows this live. |
| fps | FLOAT | 24.001–240 | Timeline frame rate. Sources at a different rate are resampled. |
| model | COMBO | free | The model this timeline feeds. Its frame count is snapped to the grid that model requires: Wan, Hunyuan Video, Kandinsky, Cosmos Predict and SCAIL use 4n+1; LTX and Cosmos 1 use 8n+1; Mochi uses 6n+1; MiniMax H3 uses 17n+5. |
| quantize_n | INT | 81–256 | Only used by 'custom (multiple of N)'. |
| start_frame | INT | 00–1000000 | — |
| frame_count | INT | 00–1000000 | 0 = up to the end of the last clip. |
| clip_audio | BOOLEAN | true | Include the videos' own audio in the mix. |
| clip_markers | BOOLEAN | false | Also emit the first and last frame of every picture clip on the 'markers' output, AHEAD of the hand-placed markers: clip 1 in, clip 1 out, clip 2 in… NKD Freeze Frames then freezes each cut's boundary frames without marking them by hand. |
Outputs (16)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| mask | MASK | The mask lane, and nothing else. |
| coverage | MASK | White where there is NO material, i.e. the stretches the editor labels 'generate'. Feed it straight to a temporal inpainting mask. For the gaps PLUS the mask lane, use 'generate'. |
| generate | MASK | Everything the model should generate: the mask lane UNION the gaps. A gap is a region to generate, so it belongs in the conditioning mask - 'mask' alone leaves it black and nothing is generated there. This is the socket for temporal inpainting. |
| audio | AUDIO | — |
| audio_mask | MASK | WHITE where the rendered soundtrack is silent: frames covered by a MUTED clip, and frames no clip gives sound to at all — silence is a region to generate, like a picture gap in coverage. One value per frame: feeds the audio mask of 😺NKD AV Latent / 😺NKD Audio Mask directly. For MVEx Audio Mask To Latent use audio_ranges instead. |
| audio_ranges | STRING | The same silent stretches as in,out second pairs (e.g. 0.292,0.833), relative to the rendered range — the exact syntax MVEx Audio Mask To Latent's time_ranges input parses. |
| width | INT | — |
| height | INT | — |
| fps | FLOAT | — |
| frame_count | INT | — |
| duration | FLOAT | Length of the output range in seconds. |
| current_frame | INT | — |
| current_image | IMAGE | — |
| markers | STRING | Comma-separated indices of the freeze-frame markers (press M on a clip), counted INTO the 'images' batch. Feed it to NKD Freeze Frames. |
| raw_images | IMAGE | The timeline cut as-is: every clip contributes its picture, including audio-only clips. Gaps with no material are black. Use as reference for models that need the original video. |