Video Frame Player
The video scrubber that crops, trims, and resizes in the graph
- frames
- frame
- frame_index
- frame_count
- processed
- out_width
- out_height
- crop_x_px
- crop_y_px
- crop_w_px
- crop_h_px
- trimmed_count
- playback_fps
- frames_trimmed
- trim_start_idx
- trim_end_idx
- width
- height
- duration
- video_info
Every video workflow in ComfyUI has the same bottleneck: you can't look at your frames without chaining Load → Preview and you can't choose a frame without guessing at an index. VideoFramePlayerMEC is the in-graph answer - a scrubber with a timeline you can drag, an interactive drag-crop overlay with eight handles, trim markers, frame stride, and integrated resize, all in one node. It plays inside the node itself, and it emits the processed batch ready to feed a sampler or a video saver.
The pitch from the author is "one node so you don't have to chain Load → Trim → Crop → Resize → Preview," and that's exactly the workflow it replaces. It's from the ComfyUI-CustomNodePacks pack, an output node, and one of the more polished pieces of the whole suite.
How it works
Feed it frames (the batch), and you get a player: drag the timeline to scrub, hit space to play, set loop mode (once / loop / ping-pong), and mark trim in/out by dragging the green and red markers on the timeline. The frame_index widget tracks your scrub position. Turn on crop_enabled and the preview grows a draggable rectangle - aspect-lockable (1:1, 16:9, 9:16, original, custom, etc.) - with rule-of-thirds guides and a live W×H readout. Then resize_method + target_width/target_height/upscale_factor produce the final output.
The critical design decision is output_mode:
current_frame(default) - emit only the selected frame. For single-frame work (grab a hero frame, test a prompt on one frame).all_frames- apply trim + stride + crop + resize to every frame, producing the processed batch. This is the mode that turns the player into a real preprocessing stage.
The inputs and outputs that matter
frames(IMAGE) - the batch.frame_start/frame_end/frame_stride- the trim range and sampling step (stride applies inall_framesmode).crop_enabled,aspect_ratio,crop_x/y/w/h- the crop, set by dragging on the preview.resize_method,target_width,target_height,upscale_factor- the resize stage.lanczosis the quality pick.playback_fps,loop_mode- playback controls.
The outputs are where this node earns its keep - it's a control room, not just a preview. The ones you'll actually wire:
frame(IMAGE) - the full-res frame at the scrubber position (pre-crop/pre-resize, full source precision).processed(IMAGE) - the crop+resize result, or the full trimmed/strided batch inall_framesmode.frames_trimmed(IMAGE) - the trimmed source batch without crop/resize. This is the one to feed a video saver when you don't want to lose source resolution.trimmed_count,out_width,out_height,duration,playback_fps- numbers for downstream automation.video_info(STRING) - a JSON blob with all the metadata (width, height, fps, duration, trim/crop values). Wire it to a Show Text node or your own automation.
That's eighteen outputs total, but you'll realistically use frame, processed, frames_trimmed, and video_info. Don't let the long list intimidate you.
Installing it
Standard pack install:
cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks.git
restart ComfyUI or use Manager → "CustomNodePacks". No heavy dependencies - the preview is browser-side; the crop/resize is standard tensor work.
Where people get burned
The most common mistake is leaving output_mode on current_frame and wondering why the "whole video" came out as one frame. If you're processing the clip, switch to all_frames. The second is forgetting that processed applies crop and resize - if you want the clip at source resolution with only a trim, wire frames_trimmed to your saver instead. And the preview_width/preview_format knobs only affect what the browser shows - the IMAGE outputs are always full source precision, so don't fear the JPEG setting.
Inputs (24)
| Name | Type | Default | Description |
|---|---|---|---|
| frames | IMAGE | Frame batch (B,H,W,C). B=number of frames. | |
| frame_index | INT | 00–99999 | Current frame to emit on the IMAGE output. Drag the timeline to scrub. |
| output_mode | COMBO | current_frame | current_frame: emit only the selected frame. all_frames: apply trim+stride+crop+resize to every frame. |
| frame_start | INT | 00–99999 | First frame of the trim range. Drag the green marker on the timeline. |
| frame_end | INT | -1-1–99999 | Last frame of the trim range (inclusive). -1 = last frame. Drag the red marker on the timeline. |
| frame_stride | INT | 11–64 | In 'all_frames' mode, output every Nth frame within the trim range. |
| playback_fps | FLOAT | 24.00.1–240 | Preview playback speed (frames per second). |
| loop_mode | COMBO | loop | Preview playback at end of trim range: once / loop / ping-pong. |
| crop_enabled | BOOLEAN | false | Enable the drag-crop rectangle on the preview. |
| crop_locked | BOOLEAN | false | Lock the crop rect to prevent accidental drags. Press R on the canvas to reset. |
| aspect_ratio | COMBO | free | Aspect lock. 'original' = source W:H. 'custom' = custom_aspect_w:custom_aspect_h. |
| custom_aspect_w | FLOAT | 16.00–999 | Custom aspect width (used when aspect_ratio = custom). |
| custom_aspect_h | FLOAT | 9.00–999 | Custom aspect height (used when aspect_ratio = custom). |
| crop_x | FLOAT | 0.0000–1 | Crop left edge as fraction of source width [0..1]. Set by dragging the rectangle on the preview. |
| crop_y | FLOAT | 0.0000–1 | Crop top edge as fraction of source height [0..1]. |
| crop_w | FLOAT | 1.0000.001–1 | Crop width as fraction of source width (0..1]. |
| crop_h | FLOAT | 1.0000.001–1 | Crop height as fraction of source height (0..1]. |
| resize_method | COMBO | none | Post-crop resize. 'lanczos' = high-quality. |
| target_width | INT | 00–8192 | Target width after crop (0 = keep crop width). |
| target_height | INT | 00–8192 | Target height after crop (0 = keep crop height). |
| upscale_factor | FLOAT | 1.000.1–8 | Multiplier applied AFTER target_width/target_height (1.0 = no upscale). |
| preview_width | INT | 48096–1920 | Width (px) of preview frames sent to the browser. Lower = lighter UI; full-resolution IMAGE outputs are unaffected. |
| preview_format | COMBO | png | png = lossless, exact colors (recommended). jpeg = smaller payload, slight chroma loss. The IMAGE outputs are always the full-precision source tensor regardless of this setting. |
| preview_quality | INT | 9530–100 | JPEG quality (ignored when preview_format=png). |
Outputs (19)
| Name | Type | Description |
|---|---|---|
| frame | IMAGE | Single full-resolution frame at the slider position (1,H,W,C) - pre-crop, pre-resize. Full source precision (no JPEG re-encode). |
| frame_index | INT | Echo of the selected frame_index (clamped to trim range). |
| frame_count | INT | Total number of frames in the input batch. |
| processed | IMAGE | Processed output: the selected frame OR the trimmed/strided batch (per output_mode), with crop + resize + upscale applied. Full source precision. |
| out_width | INT | Width (px) of the processed output. |
| out_height | INT | Height (px) of the processed output. |
| crop_x_px | INT | Crop left edge in source pixels. |
| crop_y_px | INT | Crop top edge in source pixels. |
| crop_w_px | INT | Crop width in source pixels. |
| crop_h_px | INT | Crop height in source pixels. |
| trimmed_count | INT | Number of frames emitted on 'processed' after trim+stride (1 in current_frame mode). |
| playback_fps | FLOAT | Echo of playback_fps (handy as input to video savers). |
| frames_trimmed | IMAGE | Trimmed source batch (frame_start..frame_end inclusive, with stride). NO crop/resize applied. Use this to feed downstream video savers without losing source resolution. |
| trim_start_idx | INT | Resolved trim start frame index. |
| trim_end_idx | INT | Resolved trim end frame index (inclusive). |
| width | INT | Source width in pixels. |
| height | INT | Source height in pixels. |
| duration | FLOAT | Total duration of the trimmed range in seconds (trimmed_count / playback_fps). |
| video_info | STRING | JSON string with all video metadata: width, height, fps, duration, frame_count, trim_*, crop_*, etc. Wire to a Show Text node or any downstream automation. |