AM Frame Range
Grab a slice of a batch without loading the whole thing
- image
- video
- image
- video
Stock ComfyUI is stubbornly frame-agnostic: a video is just an IMAGE batch, and there's no native "give me frames 40 through 60" utility. AM Frame Range is that utility - it slices an IMAGE batch (or a VIDEO stream) down to the frames you actually want. It's boring, and that's the point. Every long-video pipeline ends up needing one.
It ships in comfyui-am-vfx-tools ("AM VFX Tools" category), the 13-node VFX toolkit by Adrian Meyer that's the public slice of an internal studio pipeline.
How it works
You pick a frame_mode, and the node emits a sub-batch:
single- justfirst_frame. Output is a one-frame batch, shape(1,H,W,C).range-first_framethroughlast_frame, inclusive.all- pass the batch through unchanged.
Three widgets, one job. Everything is 1-based (frame 1 = the first frame in the batch), and the bounds are clamped to [1, N] so you can't slice past the end. last_frame defaults to -1, which means "end of batch" - that's the setting you'll use 90% of the time.
Why the video socket is the interesting part
The IMAGE branch is plain slicing. The VIDEO branch is where this node quietly earns its keep. Wire a VideoFromFile source (like the output of AM Read Video's video socket) into the video input, and AM Frame Range returns a lazy wrapper that only decodes the frames in your range - PyAV seeks, pulls exactly those frames, and early-terminates instead of chewing through the source's tail. Peak RAM stays at one frame, and a 10,000-frame source only costs you the 20 frames you asked for.
That's the same lazy VIDEO machinery every pixel node in this pack carries: wire image and you get a plain slice; wire video and the work gets deferred until a downstream consumer actually iterates frames. If you don't need the whole clip in RAM, use the video path.
The inputs that matter
Two, really:
frame_mode- set torangeand you're slicing;singleif you want to peek at one frame;allif you want the node as a no-op passthrough.first_frame/last_frame- your window, 1-based, inclusive.last_frame = -1= to the end.
Outputs are image (the sliced batch, or the single frame) and video (a FrameRangeVideo wrapper, or a zero-copy VideoFromComponents around the sliced batch). None when nothing's wired.
Installing it
Install the pack once, get all 13 nodes:
cd ComfyUI/custom_nodes
git clone https://github.com/am-pipeline-prod/comfyui-am-vfx-tools.git
cd comfyui-am-vfx-tools
pip install -r requirements.txt
Restart ComfyUI. Or search comfyui-am-vfx-tools in ComfyUI Manager. The pack pulls in OpenImageIO, OpenColorIO, PyAV, and OpenCV headless - the pip step installs those, and it's not optional.
Where people get burned
The 1-based indexing trips people up if they're coming from zero-based code - frame 1 is the first frame, not frame 0. And remember the clamp: if you set first_frame beyond the batch length, it clamps to [1, N] rather than erroring, so a mistyped range silently becomes the whole batch instead of failing loudly. Check the output frame_count if you're not sure you sliced what you think you sliced.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| frame_mode | COMBO | single | Which frames to emit from the input batch. single = only `first_frame`. range = `first_frame`..`last_frame` inclusive. all = pass the batch through unchanged. |
| first_frame | INT | 1-999999–999999 | Frame index in single mode; lower bound in range mode (1-based). Ignored in all mode. Clamped to [1, N]. |
| last_frame | INT | -1-1–999999 | Range upper bound (inclusive, 1-based). -1 = end of batch. Ignored in single / all modes. Clamped to [first_frame, N]. |
| imageopt | IMAGE | Image batch to slice. | |
| videoopt | VIDEO | Optional VIDEO input. When wired, returns a lazy `FrameRangeVideo` wrapper that filters frames on consumption — no IMAGE materialisation here. With a `VideoFromFile` source, PyAV decodes ONLY the frames in the requested range and early-terminates after the last requested frame is yielded. Combined with a downstream AM consumer, peak RAM stays at one frame and the source's unwanted tail frames are never decoded. `image` is ignored when `video` is wired. See invariant 28. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | Selected image batch. Shape (1,H,W,C) in single mode, (N,H,W,C) in range mode, identical to the input in all mode. |
| video | VIDEO | Lazy VIDEO output — emits a `FrameRangeVideo` wrapper when `video` is wired, else a zero-copy `VideoFromComponents` around the sliced IMAGE batch. None when no input is wired. |