Nodes/PerfectLab Studio/πŸ§ͺ PerfectLab - Shot Series
ComfyUI Node

πŸ§ͺ PerfectLab - Shot Series

Describe the person once, get the whole contact sheet

By perfectmodelslabΒ·Created 8 months agoΒ·Updated a day agoΒ· 23
πŸ§ͺ PerfectLab - Shot Series
    • PROMPT
    • WIDTH
    • HEIGHT
    • SEED
    β—„count12β–Ί
    β—„seed0β–Ί
    β—„vary_framingtrueβ–Ί
    β—„vary_posetrueβ–Ί
    β—„vary_expressiontrueβ–Ί
    β—„vary_focustrueβ–Ί
    β—„vary_ratiotrueβ–Ί
    β—„template{framing}, focused on {focus}. {scene} {person}, {hair}, {pose}, wearing {outfit} and {footwear}, {placement}, {expression}. {lighting}. {style} {quality}β–Ί
    β—„quality_lineeditorial photography, 85mm, natural lightβ–Ί
    β—„vary_hairtrueβ–Ί
    β—„vary_outfittrueβ–Ί
    β—„vary_footweartrueβ–Ί
    β—„vary_lightingtrueβ–Ί
    β—„axis_data{}β–Ί
    β—„personβ€”β–Ί
    β—„person_wideβ€”β–Ί
    β—„sceneβ€”β–Ί
    β—„styleβ€”β–Ί

    Consistency work has a boring plumbing problem in front of the interesting one. Even with your character locked - same LoRA, same person block - you still have to write "extreme close-up, sitting on a stool, arms crossed, 4:5" for shot one, then do it again by hand thirty times, keeping framing matched to aspect ratio yourself. πŸ§ͺ PerfectLab - Shot Series stops you doing that.

    You describe the person once. The node walks nine axes on its own and hands you a numbered session: seven framings, thirty-two poses, twenty-eight expressions, twelve focus targets, twenty-four hairstyles, fourteen outfits, twelve footwear options, eight lighting setups, seven formats.

    Counted, not rolled - and that's the whole trick

    This is where it differs from every "random prompt" node you've used. The axes aren't sampled, they're strided: (run * stride) % length, with the stride chosen coprime to the list length. Walk a 32-item pose list that way and you visit all thirty-two before repeating, in a fixed order that looks shuffled. A different salt per axis keeps two same-length axes from marching in lockstep - precisely the failure you'd get from a naive i % n.

    The practical consequences are good. A given seed always produces the same session, and re-running with a bigger count extends the series rather than reshuffling it - generate twelve, like them, ask for fifty more, keep the first twelve. You also get coverage: every framing, not seven variations of a close-up.

    Framing and format stay coupled: close shots land in 1:1 / 4:5 / 3:4, medium in 4:5 / 3:4 / 2:3, full-body and wide in 2:3 / 9:16 / 3:4 / 16:9 - all scaled to the same ~1.5 MP area and snapped to a multiple of 16, so the latent is always valid and your VRAM bill doesn't wander.

    The inputs that matter

    • count (1–100, default 12) is how many shots. seed fixes the session; shot i is emitted with seed seed + i.
    • template is a sentence with thirteen slots you can move or delete: {framing}, {person}, {scene}, {hair}, {pose}, {outfit}, {footwear}, {expression}, {placement}, {lighting}, {style}, {quality} - plus {focus}. Empty it and you get the built-in default line.
    • person (and scene, style) are optional text inputs - wire any text node in. person_wide is the smart one: for full-body and wide framings the node swaps to the compact description if you supplied one, because a paragraph of face detail is wasted on a two-thirds-body shot.
    • The nine vary_* booleans turn axes off. axis_data is the hidden JSON the panel's pin lists write - that's how you lock a value.
    • quality_line is appended as {quality} so you can keep your own footer text out of the template.

    All four outputs are lists: PROMPT, WIDTH, HEIGHT, SEED. Wire PROMPT into your text encode, WIDTH/HEIGHT into an Empty Latent Image (convert those widgets to inputs), and SEED into the KSampler if you want each shot on its own deterministic seed. ComfyUI's list handling does the iterating - you queue once and come back to a contact sheet.

    Install

    No Python dependencies: requirements.txt is empty on purpose and pyproject.toml lists none, so nothing can conflict with your existing environment.

    cd ComfyUI/custom_nodes
    git clone https://github.com/perfectmodelslab/ComfyUI-PerfectLab PerfectLab
    

    Restart ComfyUI and hard-refresh the tab (Ctrl+F5). Same repo as the Prompt Assistant - one clone gives you both nodes. Needs Python 3.10+.

    Where people get burned

    Unchecking a vary_* box does not freeze the value you're looking at. With vary_pose off, the node uses seed % length - a single fixed pose, but a seed-chosen one, which will not be the pose you liked. To actually pin something, click that axis row in the panel and pick from the unfolded list; that writes axis_data. This is the single most-reported misunderstanding of the node and it's baked into the logic, not a bug.

    placement is the odd one out. It isn't an axis you can pin - it cycles by index regardless of your settings. It's in the template as {placement}, so delete the slot if you don't want it.

    Two things need to match up. count decides the list length on all four outputs. If an upstream node also emits a list, mismatched lengths are how you end up queueing 96 images when you asked for 12. And if you ever route PROMPT through a node that collapses the list, the batch dies right there - the whole session becomes one image.

    Panel missing or unstyled. Restart the server (it only scans custom_nodes at startup) and hard-refresh the browser. If the node is absent entirely, check the console for IMPORT FAILED or an "An error occurred while retrieving information" message naming PerfectLabAssistant.

    Deleting template slots leaves stray commas. The node collapses runs of commas for you, but if you hand a mangled line to something strict, that's where it came from.

    CategoryPerfectLab

    Inputs (18)

    NameTypeDefaultDescription
    countINT121–100β€”
    seedINT00–18446744073709550000β€”
    vary_framingBOOLEANtrueβ€”
    vary_poseBOOLEANtrueβ€”
    vary_expressionBOOLEANtrueβ€”
    vary_focusBOOLEANtrueβ€”
    vary_ratioBOOLEANtrueβ€”
    templateSTRING{framing}, focused on {focus}. {scene} {person}, {hair}, {pose}, wearing {outfit} and {footwear}, {placement}, {expression}. {lighting}. {style} {quality}β€”
    quality_lineSTRINGeditorial photography, 85mm, natural lightβ€”
    vary_hairBOOLEANtrueβ€”
    vary_outfitBOOLEANtrueβ€”
    vary_footwearBOOLEANtrueβ€”
    vary_lightingBOOLEANtrueβ€”
    axis_dataSTRING{}β€”
    personoptSTRINGβ€”
    person_wideoptSTRINGβ€”
    sceneoptSTRINGβ€”
    styleoptSTRINGβ€”

    Outputs (4)

    NameTypeDescription
    PROMPTSTRINGβ€”
    WIDTHINTβ€”
    HEIGHTINTβ€”
    SEEDINTβ€”