Extensions/PSD Figure Creator
ComfyUI Extension

PSD Figure Creator

Load PSD files, rig layers with interactive R / MR / SW control points, and composite as IMAGE + MASK. Supports setup/pose modes, model/pose library, background options, and i18n (ja/en/zh).

By ketle-man·Created 2 months ago·Updated 2 months ago· 0
ketle-man/PSD-Figure-Creator
Nodes1
On cloudLocal install
Categoryimage/psd
Stars0
Updated2 months ago
Readme

ComfyUI PSD Figure Creator

Language / 言語 / 语言: English | 日本語 | 中文

A ComfyUI custom node for loading PSD files, rigging layers with interactive control points, and compositing the result as IMAGE + MASK outputs.


Features

  • Interactive layer viewer — toggle visibility, rename layers and custom groups
  • Custom groups — bundle layers into named groups; drag to reorder draw order
  • Rigging system — place control points on layers directly on the canvas:
    • R (blue) — rotation only
    • MR (red/orange) — move + rotate
    • LSW (green) — layer switch: rotate a handle to step through up to 12 slots; use +L to add an individual layer (1 slot), +P to add a group/folder expanded per-layer (Piece, N slots), or +C to add a group/folder composited as one (Composite, 1 slot)
    • PSW (white) — pose switch: rotate a handle to apply registered poses; register R/MR pose states to up to 12 slots in 30° increments
  • Setup mode / Pose mode — configure rigs in setup mode, animate in pose mode
  • Keyframe animation — record poses at specific frames, interpolate between keyframes (linear lerp for position, shortest-path for angles), preview playback at configurable FPS, and export as WebM video (Chrome/Edge) or transparent animated GIF. Save/load full animation projects to the library.
  • Library — save/load named model files (.psd-model.json), pose files, and keyframe animation projects
  • Background options — checker pattern / solid color / local image / upstream IMAGE node
  • Capture → Queue Prompt — bake the current canvas state to an output image
  • i18n — UI language auto-detected from navigator.language (Japanese / English / Simplified Chinese)

Screenshots

Animation output

Animation output

Node

Node overview

Node — with keyframe animation panel

Node with keyframe panel expanded

Editor — Layers tab (Setup mode)

Layers tab with MR point placement

Editor — Parent tab

Parent tab with full rig labels

Editor — Switch tab

Switch tab with SW point configuration

Node preview — rig complete

Node preview showing all rig points

Library — model & pose browser

Library panel

Capture in a ComfyUI workflow

Capture used inside a workflow

Editor — PSwitch tab (Setup mode)

PSwitch tab with PSW point configuration


Installation

# 1. Copy or symlink this folder into ComfyUI's custom_nodes directory
#    e.g. ComfyUI/custom_nodes/psd-image-loader/

# 2. Install the Python dependency
pip install psd-tools

Restart ComfyUI. The node appears under image/psd → PSD Figure Creator.


Sample Data

A ready-to-use sample is bundled in the user_data/ directory:

| File | Description | |---|---| | user_data/sample_1.psd | Sample character PSD | | user_data/models/sample.psd-model.json | Pre-configured rig (R/MR points, parent hierarchy, SW switch) | | user_data/models/sample2_lswpswset-model.psd-model.json | Pre-configured rig with LSW and PSW points | | user_data/poses/pose1.pose.json | Sample pose 1 | | user_data/poses/pose2.pose.json | Sample pose 2 | | user_data/poses/project-psw.pose.json | Sample keyframe animation project using PSW |

To use the sample:

  1. Copy user_data/sample_1.psd to ComfyUI/input/psd/
  2. Open the Editor and click 📂 modelsample will appear in the library
  3. Load the model; sample poses are available in the pose library

Upgrading from PSD Loader (≤ v2.16):
If your workflow JSON contains "PSDLoader", replace it with "PSDFigureCreator".


Moving to a New PC

Two things must be transferred separately when setting up on a new machine.

1. PSD files

PSD files live in ComfyUI's input/psd/ directory.

ComfyUI/input/psd/  →  copy to the same path on the new PC

2. Library data (models & poses)

Library data is stored in user_data/ inside the custom node folder.

ComfyUI/custom_nodes/PSD-Figure-Creator/user_data/  →  copy to the same path on the new PC

Bringing a workflow JSON: If a workflow has psd_filename set but the PSD file is missing on the new PC, you'll see a "Failed to fetch layer info" alert. Just press Setup again — the modal will open with empty layers (v0.5.2+), and you can use the Open PSD button to re-select the file.


Node Inputs & Outputs

| Parameter | Type | Description | |---|---|---| | psd_filename | STRING | PSD file path relative to the input/psd/ directory | | layer_config | STRING | JSON string produced by the UI editor | | output_width | INT | Output width in pixels, adjustable in 1 px steps (0 = native PSD size) | | output_height | INT | Output height in pixels, adjustable in 1 px steps (0 = native PSD size) | | image_data | STRING | Base64 PNG from Capture (bypasses server-side compositing) | | background_image | IMAGE | Optional upstream image composited as the bottom layer |

| Output | Type | Description | |---|---|---| | image | IMAGE | Composited RGB image | | mask | MASK | Alpha channel |


UI Overview

[✨ New] [📂 PSD file]  [⟳]
[Editor]          [⏱] [RC]
[📸 Capture]
 ┌──────────────────────────────────┐   ← keyframe panel (⏱ to toggle)
 │ [+KF][🗑KF]|[+CK][-CK]|[↔]|[0][◀][f]/[t][▶] │
 │ ◆────◆──────── timeline ────────── │
 │ [New] FPS[24] [💾Proj] [🎬WebM][🎞️GIF] [▶▶] [■] │
 └──────────────────────────────────┘
 ┌────────────────────────┐
 │  Preview canvas        │
 └────────────────────────┘
 Point Size: ─────────────
 BG: [■ color][✕] [🖼 Image][✕] [🔗 Connected?]
  • Editor — opens the full-screen setup/pose modal
  • — toggle the keyframe animation panel
  • RC — reset camera (pan + zoom)
  • ✨ New — clears all rigging, SW layers, and poses (prompts for confirmation)

Setup modal tabs

| Tab | Contents | |---|---| | Layers | Layer tree, custom group management, rig mode buttons (R / MR / LSW / PSW) | | Parent | Parent–child hierarchy for propagated transforms | | LSwitch | LSW layer list and group-slot editor | | PSwitch | PSW point list, slot management, and pose registration |


Rig System

R — Rotation

Blue dot. Drag in pose mode to rotate the layer around the placed pivot.

MR — Move + Rotate

Red origin + orange handle. Drag the handle to move and rotate simultaneously.

LSW — Layer Switch

Green origin + cyan handle. Rotating the handle steps through registered slots in 30° increments (maximum 12 slots × 30° = 360°).
Drag the origin in setup mode to reposition; drag the handle to adjust radius and initial angle.

Slot entry types (configured in the LSwitch tab):

| Button | Entry | Badge | Slots | |---|---|---|---| | +L | Individual PSD layer | [L] | 1 slot | | +P | Custom group or PSD folder (Piece) | [P] | 1 slot per member / leaf layer | | +C | Custom group or PSD folder (Composite) | [C] | 1 slot (all members rendered together) |

A slot entry whose group or folder has been deleted shows a red row background and a ⚠ icon (orphaned). Delete it manually before adding new entries.

PSW — Pose Switch

White origin + purple handle. Rotating the handle applies the registered pose for the active slot.

Usage (configured in the PSwitch tab):

  1. In Setup mode, click the PSW button (a PSW layer is created automatically on first use), then click the canvas to place a point
  2. Use +Slot to add slots in 30° increments (up to 12 slots); −Slot removes the last slot
  3. Select the slot 0 (0°) row, then in Pose mode use +MLP to register all layer poses, or select a layer in the layer tree and use +SLP to register a single layer's pose; −LP clears the slot
  4. For slots other than 0°: click the slot row → click ✏ Edit to load the pose → adjust → click ✓ Confirm to save
  5. The handle angle determines the active slot: 0°–30° → slot 0, 30°–60° → slot 1, and so on; the movement range is locked to (slot count − 1) × 30°

Multiple PSW points operate independently and their poses are composited together.

The PSW toggle button (left of the Capture button on the node) enables or disables PSW globally:

| State | Color | Effect | |---|---|---| | ON (default) | Blue | PSW preset poses are applied based on handle angle | | OFF | Red | PSW disabled — all layers (including PSW-registered ones) are freely controlled by R/MR |

The PSW ON/OFF state is saved and restored across all operations:

  • Keyframes — saved per frame; toggle switches automatically during playback
  • Pose save (📷 Pose / right-click Pose+SW) — toggle state is restored on load
  • Model save/load — toggle state is included in the model file
  • Project save (ComfyUI workflow) — persisted in layer_config

Keyframe Animation

Toggle the keyframe panel with the button on the node.

Controls

Row A

| Button / Field | Action | |---|---| | +KF | Record the current pose (visibility, position, angles) at the current frame | | 🗑KF | Delete the pose keyframe at the current frame (camera data preserved) | | +CK | Record a camera keyframe (zoom / x / y / roll) at the current frame | | -CK | Delete the camera keyframe at the current frame (pose data preserved) | | | Toggle key-move mode: when ON, drag keyframe diamonds on the timeline to move them; playhead scrubbing is disabled | | 0 | Jump to frame 0 | | / | Step one frame back / forward | | Frame input | Jump to a specific frame | | Total input | Set total frame count |

Row B

| Button / Field | Action | |---|---| | New | Clear all keyframes and reset to frame 0 (confirm required) | | FPS | Playback and export frame rate (default 24) | | 💾 Proj | Save the animation project to the library (name: project-YYYYMMDDHHMMSS) | | 🎬 WebM | Export as a WebM video file (Chrome/Edge recommended) | | 🎞️ GIF | Export as a transparent animated GIF image | | / | Start / stop playback preview ( is double-width) |

Timeline

Click or drag the timeline canvas to scrub to any frame. Recorded keyframes appear as markers.

Interpolation

| Property | Method | |---|---| | Position (tx / ty) | Linear lerp | | Rotation angle | Shortest-path angle lerp (handles 0 ↔ 360° wrap) | | SW handle angle | Shortest-path angle lerp | | PSW handle angle | Shortest-path angle lerp | | Visibility | Step: value of the previous keyframe |

Project Save / Load

💾 Proj saves keyframe data (keyframes, kf_total_frames, kf_fps) to the library's Poses panel as _type: "kf_project". Loading it from the library restores the full timeline and applies frame 0's pose to the canvas.

Keyframes are also persisted in layer_config.keyframes, so they are saved and restored with the ComfyUI workflow JSON automatically.


Clipping Layers

Layers with the Photoshop "clip to layer below" flag appear with a badge in the layer panel and SW +L dropdown. The canvas compositor renders them using source-atop blending: each clipping layer is masked to the opaque area of its base layer (the layer directly below it). R/MR rigs placed on a clipping layer work normally within that masked region. Clipping applies everywhere: at the PSD document root, inside folders, and inside custom groups.

⚠ Parent setup note: If the base layer has a rig that moves it, the clipping layer must share the same parent (configured in the Parent tab) to follow along. Without a matching parent, the clipping layer stays at its original canvas position while the base moves, breaking the mask alignment.


Background Priority

Highest to lowest:

  1. ComfyUI background_image input — server-side composite (letterbox, aspect-ratio preserved)
  2. Local background image — loaded via the 🖼 Image button, rendered client-side
  3. Background color — solid fill selected with the color picker
  4. Checker pattern — default transparent background indicator

File Structure

psd-image-loader/
├── __init__.py              # Node registration
├── psd_loader_node.py       # PSDFigureCreatorNode
├── psd_utils.py             # psd-tools compositing helpers
├── server.py                # aiohttp API routes (upload / layers / preview / library)
├── requirements.txt
└── web/
    ├── js/
    │   ├── psd_loader.js    # Front-end (canvas, modal, rigging)
    │   └── i18n.js          # Translation dictionaries + t() helper
    └── css/
        └── psd_loader.css

layer_config Schema

{
  "visibility":    { "<layerId>": true | false },
  "renamed":       { "<layerId>": "display name" },
  "custom_groups": [{ "name": "...", "layer_ids": [...], "visible": true }],
  "layer_order":   [{ "id": "...", "children": [...] }],
  "rigging": {
    "<layerId>": {
      "r":         { "x": 0, "y": 0 },
      "mr":        { "x": 0, "y": 0 },
      "mr_radius": 40
    }
  },
  "pose": {
    "<layerId>": { "angle": 0, "tx": 0, "ty": 0 }
  },
  "sw_layers": [{
    "id": "...", "name": "sw1",
    "points": [{
      "id": "...", "name": "pt1",
      "x": 512, "y": 512,
      "radius": 60, "angle": 0,
      "groups": [
        "<layerId>",                                              // +L — individual layer, 1 slot
        { "type": "custom_group", "id": "...", "mode": "piece" },     // +P — 1 slot per member layer
        { "type": "psd_group",    "id": "...", "mode": "composite" }  // +C — 1 slot (composited)
        // mode defaults to "piece" when omitted (backward compatible)
      ]
    }]
  }],
  "psw_layers": [{
    "id": "...", "name": "PSW1",
    "points": [{
      "id": "...", "name": "PSW1",
      "x": 512, "y": 512,
      "radius": 80, "angle": 0,
      "slots": [
        { "degree": 0,  "pose": null },                          // empty slot
        { "degree": 30, "pose": { "<layerId>": { "angle": 0.5, "tx": 10, "ty": -5 } } }
      ]
    }]
  }],
  "keyframes": [
    {
      "frame": 0,
      "visibility": { "<layerId>": true },
      "pose":       { "<layerId>": { "angle": 0, "tx": 0, "ty": 0 } },
      "sw_angles":  { "<pointId>": 0 },
      "psw_angles": { "<pointId>": 0 }
    }
  ],
  "kf_total_frames": 60,
  "kf_fps": 24
}

Requirements

  • ComfyUI (latest)
  • Python 3.10+
  • psd-tools ≥ 1.9.0

Troubleshooting

[INFO] Unknown image resource / Unknown tagged block in the console

[INFO] Unknown image resource 1092
[INFO] Unknown tagged block: <Tag.CAI: b'CAI '>, ...

These are informational messages from the psd-tools library (not errors). They appear when a PSD file contains metadata that psd-tools does not yet recognize — for example, resources added by recent versions of Photoshop such as Generative Fill (CAI tag). The file is still read and composited correctly; the unknown data is simply skipped. No action is required.


License

MIT