Nodes/Seb Nodes/Pano State to Camera (Seb)
ComfyUI Node

Pano State to Camera (Seb)

Five numbers out of a panorama editor's JSON

By cyberhirsch·Created about a year ago·Updated 3 days ago· 4
Pano State to Camera (Seb)
    • yaw
    • pitch
    • fov
    • roll
    • aspect
    • info
    state_json
    shot_index0

    Panorama-Stickers - nomadoor's interactive equirect editor node - lets you look around inside a 360° sphere and "take a photo," and it hands the result out as a blob of JSON. If you're driving Seb's 360 nodes, Read 360 reads that JSON itself and you never see it. Pano State to Camera is for the other case: you want the camera as plain numbers, to drive something that isn't part of that pipeline.

    So this is a small, dumb, useful adapter node. Multiline JSON in, five floats and a status line out, nothing rendered.

    What it actually parses

    It handles both shapes the Panorama-Stickers family emits. Either the compact sticker_state from Panorama Cutout, which is just {"pose": {...}}, or the full editor state with a shots list. If there's a pose, that wins. Otherwise it takes shots[shot_index], and shot_index is clamped into the list rather than throwing when it's out of range.

    From that pose it reads yaw_deg, pitch_deg, roll_deg and hFOV_deg. aspect is derived rather than copied: it's tan(h/2) / tan(v/2) from the pose's hFOV_deg and vFOV_deg, which is the ratio that reproduces their vertical FOV - so you can compute a matching view_height from a view_width. If the pose has no vFOV, it falls back to the state's source_aspect, and failing that to 1.0.

    Two normalisations are applied on the way out, and both exist to keep the values legal for the widgets they feed: yaw is wrapped into −180…180 and pitch is clamped to ±90. The pack's yaw convention is also worth stating plainly, because it's the kind of thing that quietly mirrors every extraction: positive yaw looks east, toward increasing longitude. No negation, no flipping.

    Inputs and outputs

    state_json is the whole input, as a multiline string - paste it for a one-off, or wire the string output of a Panorama-Stickers node into it. shot_index only matters when the JSON carries a shots array; it addresses one entry, 0 to 63.

    The outputs are yaw, pitch, fov, roll and aspect, all floats, plus info, a plain-text one-liner summarising what it found (shot 2 of 5 | yaw 90.0 pitch -12.0 fov 75.0 roll 0.0 aspect 1.333). The same line is printed to the ComfyUI console, which is honestly where you'll read it most. Right-click any widget on a downstream node - yaw on Read 360, a camera rotation on a 3D node - and "Convert to input" gives you a socket to wire it into.

    info also tells you when the node found nothing: an empty state_json returns 0 / 0 / 90 / 0 / 1.0 with the note empty, and JSON that won't parse returns the same defaults with unparseable state_json. No exception, no red node. That's the right call for a parser, but it does mean a dead wire looks like a perfectly valid camera pointed at the equator.

    Installing it

    It ships inside Seb Nodes, so it comes along with the pack. Manager → search seb_nodes → install → restart, or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/cyberhirsch/seb_nodes
    

    No models, no extra downloads beyond the pack's Python deps (OpenCV and friends, auto-installed on first start).

    Gotchas worth knowing

    shot_index and Read 360 can disagree. Read 360, when it reads the same state JSON, prefers the shot that's selected in the editor - it looks for the active shot id and only falls back to a positional index. This node is positional, always. If your editor state has several shots and you've clicked around inside it, the camera you extract with Read 360 and the numbers this node hands you may be two different shots. Set the index deliberately if that matters.

    It re-runs every time. Like the rest of the 360 nodes, IS_CHANGED returns NaN, so ComfyUI never caches it. Nothing to worry about here - it's arithmetic on a string - but don't expect a cached result.

    Empty versus broken. Because both fail quietly to the same defaults, the first thing to check when a view lands in the wrong place is whether state_json is actually populated. The console line settles it in a second.

    CategorySeb/360

    Inputs (2)

    NameTypeDefaultDescription
    state_jsonSTRING
    shot_indexINT00–63

    Outputs (6)

    NameTypeDescription
    yawFLOAT
    pitchFLOAT
    fovFLOAT
    rollFLOAT
    aspectFLOAT
    infoSTRING