WK Video Frame Picker
Scrub to the exact frame and pull it into your graph
- frame_image
- batch_frame_image
Why you'd want this
The single most-used trick in local video work is chaining: generate clip one, take its last frame, feed it as the starting image for clip two. The KB documents people doing it thirty times in a row to build 30-second sequences. The whole loop rests on one boring capability - grabbing a specific frame out of a file you already have.
WK Video Frame Picker is that. You pick an MP4 or WebM from ComfyUI's input folder, scrub to a frame, and it hands you that frame as an IMAGE. Mark a few frames and it hands you all of them as an ordered IMAGE batch. It's part of ComfyUI WorkspaceKit (ZiYao00, public beta), under 🧩 WorkspaceKit/Utilities.
How it works
The video widget is a dropdown built at node-registration time by scanning ComfyUI/input recursively for .mp4 and .webm, sorted case-insensitively and stored as relative paths. Absolute paths are rejected, and so is anything that tries to escape the input directory - the path is resolved and checked against the root. If there are no videos, the dropdown is empty and you get a one-item placeholder.
frame_index is 1-based and set by the visual Playhead. key_frames is a hidden STRING holding canonical JSON - a sorted, deduplicated array of 1-based frame numbers. The star button on the node toggles the current frame in or out of that set, Clear empties it, and marker dots on the timeline can be clicked to jump back. Adjacent markers stack into vertical lanes so they don't hide each other. Marker edits go through ComfyUI's normal change transactions, which is why they survive save and reload and why Undo restores them along with the video. Changing video clears them.
On the backend, decoding is done with PyAV in a single sequential pass over the stream - it walks decoded frames in order, keeps the ones whose zero-based index was requested, converts each to rgb24, and stops as soon as it has them all. Frames are stacked into a torch.float32 IMAGE batch normalized to 0–1. Two outputs: frame_image, the frame under the Playhead, and batch_frame_image, every marked frame in order.
There's a nice engine detail: this node implements IS_CHANGED returning the video file's mtime. So re-exporting a file to the same name invalidates the cache and you get the new content instead of a stale image. Most loaders don't bother; it saves a lot of "why didn't it update."
The batch_frame_image branch uses an ExecutionBlocker when no markers are set - it returns the message "No key frames selected. Add at least one Marker before using batch_frame_image." That's by design. frame_image still executes normally, so you can preview a frame while the batch branch sits blocked, and only wire the batch when you're ready.
The frontend bit
Scrubbing is a non-serializing DOM widget: a current-frame video preview, a 12-cell filmstrip generated in the browser, and a draggable Playhead with ◀ 1 / 1 ▶ buttons. Moving the Playhead only updates the preview; the committed frame_index changes once, on pointer-up. Only video and frame_index end up in the saved widget values.
One caveat: those thumbnails are produced by the browser, not the decoder, so a populated filmstrip doesn't prove the file will decode cleanly.
Inputs and outputs
- video - dropdown of MP4/WebM files under
ComfyUI/input. - frame_index - 1-based, the frame the Playhead is on.
- key_frames - hidden JSON marker set; you never type in it, the buttons write it.
Outputs: frame_image and batch_frame_image, both IMAGE. Wire frame_image into a VAE Encode or an I2V image input for chaining; wire batch_frame_image into a Save Image or a batch-inspection node when you're harvesting a set of keyframes for LTX-style first/last-frame work.
Install
Manager: search WorkspaceKit or comfyui-workspacekit, install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/ZiYao00/ComfyUI-WorkspaceKit.git
cd ComfyUI-WorkspaceKit
python -m pip install -r requirements.txt
Windows portable, run from the ComfyUI root:
.\python_embeded\python.exe -m pip install -r .\ComfyUI\custom_nodes\ComfyUI-WorkspaceKit\requirements.txt
That requirements file contains exactly one package: send2trash, for the sidebar's trash. It does not install PyAV. The node relies on the av package that modern ComfyUI already ships as part of its official video stack, and if it's missing you get a clear runtime error telling you to use a build that includes it. So update ComfyUI rather than pip-installing video libraries by hand. Restart after install - the picker's whole UI is a frontend extension and won't exist until the browser reloads it.
Where it bites
- Empty video dropdown. Nothing MP4/WebM in
ComfyUI/input. Subfolders are scanned, so organise as you like. - "V1 interactive preview supports MP4 and WebM files only."
.mov,.mkv,.aviare refused at validation time, even though PyAV could decode them. Remux to MP4. - "Requested frame(s) outside the decodable video range." Your marker or Playhead points past the end. The 1-based numbering trips people up constantly.
- "No key frames selected…" Not an error - add a marker, or stop using that output.
- Path errors. Everything must live inside the ComfyUI input directory; anything else is rejected on purpose.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| video | COMBO | 1 options: | |
| frame_index | INT | 11–10000000 | 1-based frame selected by the visual Playhead. |
| key_frames | STRING | [] | WorkspaceKit Marker state as canonical 1-based JSON frame indices. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| frame_image | IMAGE | — |
| batch_frame_image | IMAGE | — |