OpenPose 3D Poser
Pose a 3D Dummy Inside the Node and Hand ControlNet an Exact Skeleton
- pose_data
- reference_image
- pose_image
- pose_keypoint
The pose you want isn't in a stock photo
Pose is the one ControlNet condition that survived the 2023–2026 churn intact - every union since SDXL ships a pose channel, and the 2026 Z-Image and Flux 2 unions still list it next to canny and depth. The annoying part was never the model, it was getting the skeleton.
The usual route is to find a photo of somebody doing approximately what you want and run it through comfyui_controlnet_aux to extract keypoints. That works right up until you want the left arm raised 40 degrees, the head turned, and the shot framed from below - and now you're asking a friend to stand in your kitchen.
OpenPose 3D Poser (class OpenPose3DPoser, category image/pose) fixes that part. It's a three.js mannequin rig living inside a ComfyUI node - pose the limbs, place the camera, get an OpenPose control map out the other end. No API, no key, no model download for the poser itself.
How it works
The browser holds the rig - a bone hierarchy per person - and serialises 3D keypoints plus a camera (position, target, vertical FOV). openpose_render.py then projects those points through the same pinhole model a three.js PerspectiveCamera uses and draws them in controlnet_aux's OpenPose colours, far people first.
Because projection happens server-side, linking width/height from another node still gives a correct frame. Strokes scale against a 512px baseline, so a 1024-wide map looks like the standard map instead of a blobbier one. The whole scene lives in the hidden pose_data widget, so a saved workflow carries the pose with it.
Actually posing it
Left-drag orbits, right-drag pans, wheel zooms. Click a joint, drag the gizmo rings to rotate - every joint works, down to each finger segment. Move gives you orange IK handles: drag a wrist or ankle diamond to swing a whole limb, drag the pelvis ring to move the person. Pose… and Hands… drop in presets, and + adds a second person.
The inputs that matter
pose_data is required and typed POSER_SCENE, but you never touch it - the viewport writes it. A blank editor means the widget failed to restore, not that you should paste JSON.
width and height (default 512×768) set the output size and, importantly, the camera view's aspect ratio. include_hands and include_face toggle the 21-point hands and 70-point face; line_thickness is a stroke multiplier you can mostly leave at 1.
reference_image is optional and does one thing: after the node runs, the image comes back behind the skeleton in Camera view so you can align to a layout. It's a post-run preview - nothing appears until you queue.
Wiring the outputs
pose_image goes into Apply ControlNet as the pose condition. pose_keypoint is the OpenPose JSON in the exact structure comfyui_controlnet_aux produces - pose_keypoints_2d, face_keypoints_2d, both hand arrays, canvas dimensions - so it drops into workflows that already consume extracted keypoints.
Install
Manager, or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/roborg/comfyui-poser
Restart. There's genuinely nothing to pip install - the pack ships dependencies = [] - and the frontend is prebuilt, so no npm either. You do need a recent ComfyUI (v0.32.0 or newer, frontend 1.48+), and it's tested on both the classic canvas and the Vue "Nodes 2.0" renderer, which is more than most frontend-heavy packs managed. Demos live under Templates → Extensions → comfyui-poser; Z-Image Turbo wants about 26 GB of models, Krea 2 Turbo roughly 17.6 GB plus the Ostris Krea 2 pack. The poser itself needs none.
Framing and strength, where results are won
Only a flat 2D map reaches the model, so a pose has to read in silhouette. A kneel seen dead-on from the front looks like standing, and the model will cheerfully generate standing.
The author's own testing is worth stealing. On Z-Image Turbo with the Fun Union ControlNet 2.1-2602-8steps, strength 1.0 is correct - at 0.8 it was barely better than v1. On the Krea 2 OpenPose LoRA route, the Ostris nodes scored 97.5% of keypoints on target in kv_cache reference mode against 75% with it off, and went blotchy without it.
Things that go wrong
A black pose image means an empty scene - usually an API submission without the pose_data JSON, or a widget that didn't restore. The node also isn't an output node, so it only runs when your sampler chain is queued.
Hands and face are a lot of points to spend. With the subject small in frame, 70 face dots turn into mush the ControlNet reads as noise, so include_face off often gives a cleaner head. And keep the node big enough to see - it self-sizes large, and shrinking it until the viewport disappears is a self-inflicted way to lose the gizmos.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| pose_data | POSER_SCENE | — | |
| width | INT | 51264–8192 | — |
| height | INT | 76864–8192 | — |
| include_hands | BOOLEAN | true | — |
| include_face | BOOLEAN | true | — |
| line_thickness | FLOAT | 1.000.25–4 | — |
| reference_imageopt | IMAGE | Shown behind the skeleton in camera view after the node runs. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| pose_image | IMAGE | — |
| pose_keypoint | POSE_KEYPOINT | — |