Nodes/Old-Time Radio/ Character Photo
ComfyUI Node

Character Photo

Character Photo for Old-Time Radio

By jbrick2070·Created 7 months ago·Updated about 19 hours ago· 16
Character Photo
  • photos_in
  • character_photos
◄name►
◄image▾►

The face problem shows up the moment your radio play gets pictures. ComfyUI-OldTimeRadio writes, casts, voices and scores a whole episode locally, then draws a still per beat - and without help, the lead in beat three is a different person from the lead in beat one. The pack's answer is a reference photo, not a trained LoRA.

OTR_CharacterPhoto is the node that hands that photo over. It's small on purpose: a name, a picture, a chain socket.

What it actually does

You give it a photo of a face and the name of a character. The image phase paints that character's portrait from the photo, in whatever visual style the episode rolled or pinned, and every still of them - plus anyone sharing their frame - follows the portrait.

Which means your photo never appears in the episode. It isn't composited, it isn't a face swap, it isn't fed forward as a still. It's identity conditioning: the portrait is a generated image in the episode's style, and the photo only steers it. If you wanted a real photo of a real person in the frame, this isn't that, and no pack node is.

How the chain works

One node holds one photo, which sounds annoying until you see why: ComfyUI only gives the first required image picker an upload button, and an optional picker silently defaults to whatever sorts first in your input folder. Hence the chain.

photos_in takes the previous node's output; the last one wires into character_photos_in on OTR_ImageDirector. Under the hood it's a plain dict - {"version": 1, "photos": {"Jeffrey": ["headshot.png"]}} - accumulating as it travels. Repeat a name across nodes and you're handing the model up to three pictures of one person to average into a portrait. Three is the ceiling; the rest is dropped and reported.

The three inputs worth knowing

name is the character's name as the story uses it. Matching folds whitespace and case - "jeffrey" finds "Jeffrey" - but it doesn't guess. A name nobody in the cast has gets reported and the photo goes unused, and so does a name two characters share. Two doctors in one cast? This node can't pick for you.

image is the picker: choose a file already in ComfyUI's input folder, or upload/paste one into the widget (pastes land in pasted/). A clear face beats a gorgeous photo of someone turned three-quarters away.

photos_in is the chain socket described above. One output, character_photos, which only ever goes to Image Director.

One behaviour worth internalising: blank name plus blank picture is a pass-through. The canonical workflow ships one of these wired and empty, so the socket exists on every machine and a shared graph never names a file your disk lacks. Give it only a name, or only a picture, and validation refuses the run before it starts.

Installing it

cd ComfyUI/custom_nodes
git clone https://github.com/jbrick2070/ComfyUI-OldTimeRadio
python -m pip install -r ComfyUI-OldTimeRadio/requirements.txt

Or in ComfyUI: Extensions → Node Manager, search old time radio (registry id comfyui-old-time-radio), install, restart. The Windows portable ships without Manager, so the clone route is the honest one there.

Two install traps, both from the README: run that pip install with ComfyUI's interpreter, not a system Python - the failure mode is nodes that silently refuse to load much later, not an error. And Python 3.10–3.13 only; 3.14 has no Kokoro voice build.

This node needs nothing heavy itself, but its purpose does: your still engine must accept a reference image, and here only flux3_klein_ref does - FLUX 3 Klein with references always on, ~10 GB if you don't have those weights. The 16 GB reference workflows (otr_16gb_foley_flux3_ref and its open-weights twin) select it; a random model roll never will.

Where people get burned

The silent one: photos are reported, not honoured, on any other still engine. Leave the character still model on the canonical's z_image_turbo and the run finishes happily with the photos you handed it completely ignored. The log says so, but it's easy to miss.

The rest are literalness, not bugs. A file outside ComfyUI's input folder is "not a picture in ComfyUI's folders." A name that doesn't match the cast is a typo to hunt down. And if you also typed Name = file lines into Image Director's own character_photos widget - which the app view writes - those typed lines win for any name both give.

One honest caveat: reference-based consistency is strong, not absolute - these approaches drift on identity across many beats where a trained LoRA wouldn't. So this buys continuity, not a guarantee. Over dozens of stills that must match exactly, you're in LoRA territory. For a four-minute play where faces just have to hold between scenes, it's the cheaper answer, and it's what this pack ships.

CategoryOldTimeRadio/v2/image

Inputs (3)

NameTypeDefaultDescription
nameSTRINGThe character's name as the story uses it, e.g. Jeffrey. Empty (with no picture) passes the chain through.
imageCOMBOTheir photo: pick, upload or paste one. A clear face works best.
photos_inoptOTR_CHARACTER_PHOTOSThe previous Character Photo node, to chain them.

Outputs (1)

NameTypeDescription
character_photosOTR_CHARACTER_PHOTOS—