Nodes/ComfyUI_KleinAngleSelector/πŸŽ₯ B3D Klein Angle Selector
ComfyUI Node

πŸŽ₯ B3D Klein Angle Selector

Stop squinting at your prompt β€” pick the camera angle on a 3D globe

By Bisnis3dΒ·Created 4 months agoΒ·Updated 4 months agoΒ· 2
πŸŽ₯ B3D Klein Angle Selector
    • selected_angles
    β—„selected_indices[]β–Ί

    You've typed "back-left quarter view high-angle shot" into a prompt and had exactly zero idea whether that's the shot you wanted. That's the problem this node exists to kill. πŸŽ₯ B3D Klein Angle Selector is a little 3D hemisphere you drag around, click a camera marker on, and get back a ready-made prompt fragment for the lovis93/Flux-2-Multi-Angles-LoRA-v2 LoRA - the one that brought real camera-angle control to Flux 2 Dev back in late 2025.

    The catch you should know up front: this node doesn't render anything and doesn't touch your image. It's a prompt-string generator with a fancy face. It outputs the text that makes the LoRA move the camera. If you don't have that LoRA loaded (on the FLUX.2-dev base, not Klein - the pack's name is a red herring), the node is just making nice sentences nobody will obey.

    What it actually does

    The 3D viewer is a ComfyUI frontend extension (Three.js in an iframe, inlined in the JS) rendering a stylized subject on a hemisphere. Around it sit 72 camera markers - 8 azimuths Γ— 9 elevations - plus a radio selector for the 3 distances: close-up, medium shot, wide shot. You drag to rotate, scroll to zoom, and click markers to toggle them. Selected cameras light up with one colored dot per distance, so you can mix a wide shot and a close-up of the same camera position in one selection.

    Under the hood it's a flat pool of 216 combinations (8 Γ— 9 Γ— 3). The viewer just writes the flat index of each pick into the node's one input; the Python side does the translation.

    The one input and the output

    • selected_indices - a STRING holding a JSON array of ints, default []. It's hidden behind the viewer but stays editable, which is handy for debugging: you can hardcode [0, 27] and get the exact same output without touching the 3D scene.
    • selected_angles - a STRING list of prompt fragments, one per selected combination, deduped and in canonical order. That OUTPUT_IS_LIST = True matters: the output is a list, so you wire it into anything that loops over inputs (a Make List / For Each pattern, or a batcher) to render one image per angle in a single run.

    Each string follows the LoRA's own taxonomy: <sks> {azimuth} {elevation} shot {distance}. So <sks> right side view high-angle shot medium shot is a real output, and the <sks> token is the LoRA's trigger. Pick nothing and you get a single default: <sks> front view eye-level shot close-up.

    Installing it

    No Python dependencies at all - no requirements.txt, nothing heavy. Just:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Bisnis3d/ComfyUI_KleinAngleSelector
    

    Then restart ComfyUI, or use ComfyUI Manager and search "ComfyUI_KleinAngleSelector". The heavy lifting is elsewhere: you still need FLUX.2-dev (the ~53GB fp8 stack, a big-card affair - the KB has the full breakdown) and the Multi-Angles LoRA v2 from Hugging Face. Budget that, not the node.

    Where people get burned

    The viewer loads Three.js from cdnjs at runtime, so your first use needs internet - offline, you'll see the red "Failed to load three.js from CDN" error baked into the iframe. The node still works if you're happy to type indices by hand, but the fun part is gone.

    Two more traps. First, the angle vocabulary is the LoRA's vocabulary - "steep-mid" and "mid-low" aren't generic photography terms, they're this LoRA's buckets. Expecting the same control from a different model won't work. Second, a list output doesn't do anything by itself: until you push it through a per-item encoder, the whole node feels dead. Wire the list into a loop node first, and confirm the LoRA is actually active, or you'll be generating 216 copies of the same camera.

    Categoryflux2/camera

    Inputs (1)

    NameTypeDefaultDescription
    selected_indicesSTRING[]β€”

    Outputs (1)

    NameTypeDescription
    selected_anglesSTRINGβ€”