Nodes/ComfyUI-OldTimeRadio/ ImageDirector (per-role image model + granularity)
ComfyUI Node

ImageDirector (per-role image model + granularity)

Planning every still before a single image is drawn

By jbrick2070·Created 5 months ago·Updated about 21 hours ago· 7
ImageDirector (per-role image model + granularity)
    • image_policy_json
    announcer_granularityper_object
    music_granularityper_object
    character_granularityper_object
    fresh_cap15
    seed_moderequest_hash
    request_seed0
    video_policy_json
    custom_models_json{}
    gate_in
    dtype_policyfp8_ok

    OTR_ImageDirector is a planning node: it doesn't generate a single pixel. It turns the video plan and your granularity choices into a policy - the JSON that tells OTR_MetaBriefImagePromptGen and OTR_ImageGenDispatcher what to draw, how fresh each image must be, and how many fresh renders the episode is allowed. It's the image-side version of the "audio is king" discipline: decide before you spend GPU, then spend exactly what you planned.

    The first thing to get straight, because the display name almost lies: this node does not pick the image models. The per-role image-model picks come from OTR_VideoDirector, which you wire in as video_policy_json - and that input is required, empty/malformed fails closed. ImageDirector reads that frozen route and adds the granularity, cap, and seed layer on top. Wire the provider before this consumer, or this node refuses.

    How it works

    The heart of it is granularity. Three roles - announcer_granularity, music_granularity, character_granularity - each default to per_object:

    • per_object - one image reused per character/prop. Cheapest, and the only mode 3D roles allow.
    • per_beat - a fresh image per beat, capped to the audio-derived beat budget. Prettier, pricier.

    Then fresh_cap (15) puts a hard ceiling on fresh per-beat renders per episode - the dispatcher additionally caps to the audio beat budget, so the rule is never over-generate. seed_mode defaults to request_hash (deterministic renders from the request), with fixed as the alternative; request_seed is deliberately not named seed (a naming-discipline rule in this pack, V-7).

    custom_models_json is the escape hatch: when a role is set to "+ Add Custom Model", map the role key to your engine id, e.g. {"music_image_model":"z_image"}. dtype_policy (fp8_ok default) controls which quantized lanes are allowed - fp8/fp4 artifacts are off on ROCm/MPS tiers.

    Output: a single image_policy_json STRING that carries engine-per-role, granularity, and seed downstream. That's it - wire it to both the prompt-gen node and the dispatcher.

    Install and gotchas

    Pack-level install (ComfyUI Manager → ComfyUI-OldTimeRadio, restart; v2.0-alpha branch). No models - pure policy logic. The gotcha is wiring order: video_policy_json must come from a OTR_VideoDirector that has already frozen the route, and an empty string fails closed with a named error. If you're getting refused at this node, check the provider first, not the policy. And don't set a tiny fresh_cap and wonder why per-beat looks identical to per-object - the cap is the budget, and under it the dispatcher may legally reuse.

    CategoryOldTimeRadio/v2/image

    Inputs (10)

    NameTypeDefaultDescription
    announcer_granularityCOMBOper_objectHow often announcer beats get a fresh still: per_object = one reused image (cheapest), per_beat = fresh per beat, capped by fresh_cap and the audio beat budget.
    music_granularityCOMBOper_objectHow often music-card beats get a fresh still: per_object = one reused card, per_beat = fresh per cue, same caps as announcer_granularity.
    character_granularityCOMBOper_objectper_object: one image reused per character/prop (cheapest). per_beat: a fresh image per beat, capped by fresh_cap and the audio-derived beat budget -- more variety, more render time.
    fresh_capINT151–512Ceiling on FRESH (per_beat) renders per episode; the dispatcher additionally hard-caps to the audio-derived beat budget (never over-generate).
    seed_modeCOMBOrequest_hashrequest_hash: seeds derive deterministically from each request (same episode re-renders byte-identical stills). fixed: every still uses request_seed verbatim -- for A/B comparisons.
    request_seedINT00–4294967295Base seed (NOT named 'seed' on purpose, V-7).
    video_policy_jsonSTRINGOTR_VideoDirector policy (REQUIRED; wire the provider before this consumer). Carries the frozen route, device policy and per-role aspects; empty/malformed FAILS CLOSED.
    custom_models_jsonoptSTRING{}When a role is '+ Add Custom Model', map the role key to a custom engine id, e.g. {"music_image_model":"z_image"}.
    gate_inoptSTRINGOptional ordering signal (opaque STRING).
    dtype_policyoptCOMBOfp8_okDtype lanes allowed for local image engines (fp8/fp4 artifacts are OFF on ROCm/MPS tiers).

    Outputs (1)

    NameTypeDescription
    image_policy_jsonSTRING