Nodes/Melite Poser Nodes/Skin Sidecar (controller sidecar from kind knobs)
ComfyUI Node

Skin Sidecar (controller sidecar from kind knobs)

Turn a skin 'kind' into the JSON your controller actually reads

By JayDataEngineer·Created 2 months ago·Updated 5 days ago· 1
Skin Sidecar (controller sidecar from kind knobs)
    • sidecar
    ◄kindkeeper►
    ◄size512►
    ◄seed20260906►

    This is a metadata node, and the honest pitch is that it exists so you don't hand-write JSON. You pick a skin kind from a fixed list of twenty, plus a map size and a seed, and you get back a JSON string describing that skin: its tint, its texture family, whether it's humanoid or creature, its UV convention, and the shader law to apply.

    Downstream - in the pack's own Unity controller and material pipeline - that string is what actually decides how a character's skin looks. Tint it, set roughness, decide whether to apply triplanar detail. The node is the runtime derivation of that contract, so there's one source of truth instead of a hardcoded blob sitting next to the graph.

    The kinds

    Twenty of them, sorted, and a typo is refused at prompt validation rather than at queue time - a nice property of using a combo instead of a free-text field. Humanoids first: keeper, ash_nomad, villager, villager_elder, caravan_trader, amf_warden, cherusci_scrivener, marrow_raider. Then the creatures, which run from the mundane (dune_hound, moss_elk, gloom_tortoise) to the odd (amalgam_horror, bone_crane, ironvein_construct, emberwing, husk_stalker, ridge_lizard, sand_skimmer, arena_factor, meld_creature).

    Each kind carries a tint triple, a texture identifier (real named skin families for the humans - young_caucasian_male, old_caucasian_male and so on - and flat_placeholder for the creatures), and a category.

    What comes out

    A JSON object with the kind, tint as [r, g, b], the texture and category strings, uv: "somax", the size and seed you set, and then the shader law keys: normal_strength: 0.0, smoothness: 0.18, metallic: 0.0, triplanar_density: 3.0. Keys are sorted, so two identical runs produce byte-identical strings - which is what makes it diffable and loggable.

    Note the normal_strength: 0.0. That's the same "bump off" decision the pack makes elsewhere (SkinApply writes normalTexture.scale: 0.0), for the same reason: UV-path bump through projection tangents mottles the skin. The sidecar is where that law is published.

    The source describes it as the twin of an estate skin_spec.sidecar, with a parity wall pinning all twenty rows equal - meaning the in-pack version and the internal version are tested to produce the same output. Same pattern as the fit rows: one contract, two doors.

    Inputs and outputs

    • kind (default keeper) - one of the twenty.
    • size (default 512, 256–2048) - must be in the closed vocabulary of 256/512/1024/2048, same as SkinPack.
    • seed (default 20260906, 0 to 2^31−1) - non-negative.

    One output: sidecar, a STRING of JSON. Not an output node - nothing runs until something consumes it.

    Install

    cd /path/to/ComfyUI/custom_nodes
    git clone https://github.com/JayDataEngineer/melite-poser-nodes.git
    

    Restart, or install melite-poser-nodes from ComfyUI Manager. Pack deps (py-soma-x[anny], trimesh, xatlas) are irrelevant here - this is dict lookups and json.dumps. One of the cheapest nodes in the pack to adopt.

    Where people get burned

    Nothing in a stock ComfyUI graph consumes it for you. The sidecar's real reader is the pack's estate-side compose and controller; inside a plain graph you'll feed it to a text/save node so it lands on disk beside the asset. If you were expecting it to change the render, it won't - SkinPack and SkinApply do that. This is the record of intent.

    kind is a closed set you may not have a body for. The kinds come from the author's game project; picking moss_elk doesn't make anything elk-shaped. It sets tint, texture family and the shader law, nothing more.

    Refusals are runtime, not validation, for size and seed. The combo catches a bad kind, but 300 is a legal integer in the widget and illegal in the code:

    SkinSidecar refused: size 300 not in [256, 512, 1024, 2048]
    

    JSON consumers must tolerate a JSON string, not an object. If you're wiring it into a custom node expecting to index into a dict, parse it first - it arrives as text, with sorted keys so you can compare two runs by eyeball.

    It's estate-shaped. The keys reference a controller and a UV convention (somax) that only mean something in this author's stack. Useful if you're building a similar pipeline or want a deterministic skin-descriptor to log next to your assets; not useful as general-purpose metadata for other engines.

    CategoryTechNoir/Mesh

    Inputs (3)

    NameTypeDefaultDescription
    kindCOMBOkeeper20 options: amalgam_horror, amf_warden, arena_factor, ash_nomad, bone_crane, caravan_trader, +14
    sizeINT512256–2048—
    seedINT202609060–2147483647—

    Outputs (1)

    NameTypeDescription
    sidecarSTRING—