Nodes/ComfyUI_RetroTape/Retro Tape (VHS / NTSC)
ComfyUI Node

Retro Tape (VHS / NTSC)

Run your AI footage through a VCR that's been in a hot attic

By SKBv0·Created 3 days ago·Updated 3 days ago· 10
Retro Tape (VHS / NTSC)
  • image
  • image
settings_json{"color_drift":0.18,"luma_smear":0.2,"interlace":0.24,"vertical_roll":0.05,"analog_glow":0.16,"saturation":1.0,"black_lift":0.0,"preset":"RGM","blend":1.0,"seed":0,"video_noise":3900,"chroma_noise":9800,"chroma_phase_noise":14,"chroma_loss":180,"color_bleed_h":2,"color_bleed_v":0,"ringing":0.72,"ringing_power":4,"vhs_enabled":true,"vhs_edge_wave":2,"head_switching":true,"scanline_phase":90,"scanline_offset":0,"chroma_delay":4,"tracking_jitter":2.2,"tape_warp":2.5,"dropouts":0.14,"ghosting":0.2,"scanlines":0.34}
frame_offset0
deviceauto

Everyone's Wan clip is sharp, clean, correctly graded and completely, obviously AI. The cheapest fix isn't a better model - it's ruining the picture on purpose, the way a thirty-year-old tape would. Retro Tape does that: NTSC composite and VHS damage on a still or a frame batch, with the preview inside the node so you can feel for the look instead of queuing a render.

What you're actually doing here

The post-processing doc makes the argument better than I can: grain and tape grime are the cheapest move for breaking the AI look, and it's a deterministic pixel operation, not a generation. No diffusion pass, no seed lottery, no re-rolling the face you finally got right. The usual alternative is ntsc-rs outside ComfyUI, via a node that dumps frames through ffmpeg, shells out and reloads them - it works, and it's a round-trip. This runs in-process.

Lineage explains the quality: adapted from ntscQT (JargeZ), itself built on Jonathan Campbell's composite-video-simulator. Four of the presets are ntscQT community templates, copied verbatim.

How it works

It converts the frame into YIQ - the colour space NTSC actually used, luma separate from two chroma channels - then damages it like a real signal chain. Chroma gets low-pass filtered per scanline, which is where the smeared colour edges come from. Then ringing, noise, ghosting, the VHS edge wave, a tracking-warp servo, interlace and vertical roll, luma smear, dropouts, scanlines, head switching, and back to RGB through an analog-glow pass.

Two details make it look like footage rather than a filter. Every spatial constant is authored against 720×480 and scaled to your frame, so a 1080p clip and a 512px square degrade proportionally. And the slow mechanics - warp, dropouts, edge movement - are held for 4-frame epochs with their own RNG while per-frame grain is redrawn, so artefacts drift the way tape does instead of flickering independently.

Inputs and output

Four inputs, and you only really set one.

image - the IMAGE tensor, single image or batch. RGBA keeps its alpha; only colour channels get hit.

settings_json - the whole panel, stored as a JSON string. You don't type in it; the node's UI writes it.

frame_offset - leave at 0. Set it to the frame a later pass starts at when you render a long clip in chunks, or the tape damage restarts mid-shot and the seam shows.

device - auto (default), cpu, gpu. Same look either way, and the same artefact placement for a given seed, but the grain comes from a different generator on each path, so a seed isn't reproducible across devices. Pick one and keep it for the whole video.

Output: one image. It's an output node, so it runs and previews with nothing wired downstream - don't forget to also connect it to a save. The widgets to set are preset, blend (pull back toward the untreated source when a preset is too much) and seed; below that, video_noise, tracking_jitter, tape_warp, dropouts, scanlines and interlace hold the personality.

Installing it

Manager, or two commands:

cd ComfyUI/custom_nodes
git clone https://github.com/SKBv0/ComfyUI_RetroTape
pip install -r ComfyUI_RetroTape/requirements.txt

The requirements are numpy, opencv-python-headless, scipy and Pillow - things most installs already have, and no models to download. Restart ComfyUI and you'll find Retro Tape (VHS / NTSC) under the RetroTape category. The metadata asks for ComfyUI 0.3.40 or newer, so on an old checkout update ComfyUI before blaming the node. Given the dependency mess custom nodes normally cause, a pack that adds nothing exotic is worth saying out loud.

Presets, and the preview

19 presets, grouped in the dropdown: the four ntscQT community ones (RGM is the default, plus Master Nama, JaneLon, yobelkcip), and fifteen house looks under Everyday tape, Off air, Monochrome, Stylised and Worn out - clean era-correct tape, off-air broadcast, mono security-cam and kinescope, stylised, and two for a tape at the end of its life. Start from whichever is closest and nudge.

The preview uses the same pipeline as the output, and renders immediately when the image comes from Load Image, or from ComfyUI's Load Video via GetVideoComponents, or VHS_LoadVideo / VHS_LoadVideoPath. Any other graph needs one run first. There's A/B against the source, Refresh, Reset to the preset, Copy/Paste and Live/Paused. Stills up to about three megapixels preview at their own resolution, so what you see is the output.

Where people get stuck

Speed isn't one of them. The README's numbers: 1080p single frame ≈ 22 ms on GPU, ≈ 363 ms on CPU; 120 frames at 1024² ≈ 1.4 s on GPU, 11.1 s on CPU. If gpu is selected without CUDA it warns once and renders on the CPU, so "it's slow" usually means that. On high-core boxes the CPU path already parallelises frames.

The real trap is overdoing it. Full-strength dropout, warp and head-switching on a clean render reads as filter, not footage. What sells the illusion is a plausible amount of degradation plus the rest of the de-AI stack - grain, a little motion blur, a vignette. Precious few of those sliders should ever hit 100%. One more: the preview only reads source video from ComfyUI's input, output and temp folders, so point RETROTAPE_EXTRA_PREVIEW_ROOTS at wherever your footage actually lives.

CategoryRetroTape

Inputs (4)

NameTypeDefaultDescription
imageIMAGE
settings_jsonSTRING{"color_drift":0.18,"luma_smear":0.2,"interlace":0.24,"vertical_roll":0.05,"analog_glow":0.16,"saturation":1.0,"black_lift":0.0,"preset":"RGM","blend":1.0,"seed":0,"video_noise":3900,"chroma_noise":9800,"chroma_phase_noise":14,"chroma_loss":180,"color_bleed_h":2,"color_bleed_v":0,"ringing":0.72,"ringing_power":4,"vhs_enabled":true,"vhs_edge_wave":2,"head_switching":true,"scanline_phase":90,"scanline_offset":0,"chroma_delay":4,"tracking_jitter":2.2,"tape_warp":2.5,"dropouts":0.14,"ghosting":0.2,"scanlines":0.34}
frame_offsetoptINT00–1000000Where this batch starts in a longer clip. Tape artefacts follow the frame number, so a clip rendered in several runs needs each run's start here or the artefacts restart and the seam shows.
deviceoptCOMBOautoWhere the effect runs. auto uses the GPU when CUDA is available and the CPU otherwise. The two paths give the same look and put the tape damage in the same places for a given seed, but the grain is drawn from a different generator, so a seed does not reproduce the same grain pattern across devices. The preview follows this setting.

Outputs (1)

NameTypeDescription
imageIMAGE