Body Decal Apply (Advanced)
The one node that sticks tattoos, logos and scars onto bodies so they don't look pasted on
- image
- decal
- pose_keypoint
- user_region_mask
- clip_mask
- depth_map
- displacement_map
- decal_alpha_mask
- image
- mask
- debug_image
This is the whole pack in one node: pose-aware placement, masking, perspective warp, displacement, and blending, all stuffed into a single "Body Decal Apply (Advanced)" box. If your goal is "put this chest tattoo on that character and make it look like it was always there," start here instead of stitching together the modular pipeline by hand. It's the workflow the README points most users to, and honestly that's the right call - you can wire the five-node version later once you know which dial you actually want to touch.
It's called "Advanced" because the input list is a wall - around forty widgets. Breathe. The author groups them with STRING inputs that are just section headers ("- Placement / Body Region -", "- Region Mask -", "- Decal Size / Rotation -"), and those header inputs do nothing except organize the UI. Ignore them entirely. What actually matters is a handful of widgets plus the optional inputs at the bottom.
How it works
The pipeline is: detect body → derive a placement region → size and warp the decal onto it → blend. Detection expects a pose_keypoint (POSE_KEYPOINT) input from DWPose - wire in a DWPreprocessor from ComfyUI ControlNet Aux Preprocessors, the same preprocessor family the KB treats as required for any serious pose work. From the keypoints it computes a body-part axis (say shoulder→elbow for upper_arm), builds a quad around it using the region_* widths, then resizes your decal to match the region's length.
From there the warp_mode pick: rotate_paste just rotates and drops the decal near the region anchor; perspective_quad does an actual projective warp so the decal leans with the limb instead of staying perfectly front-facing. Perspective is the one that makes chest and sleeve tattoos read as real. Alpha handling comes next (alpha_mode: use_image_alpha for PNGs with alpha, black_to_alpha/white_to_alpha for logos on solid backgrounds), then optional displacement and depth warps, then the final blend with a chosen blend_mode and opacity.
One behavior worth knowing before you pull your hair out: if no person is detected in the pose data, the node silently returns your image unchanged with an empty mask. No error, no warning. That's why the debug_image output exists - it draws the region axis, anchor points and quad so you can see what the node thinks it's aiming at.
The inputs that actually matter
imageanddecal- base photo and the thing to stick on it.placement_region+placement_side- where on the body, and which side from the viewer's perspective. Eight regions: nose, neck_side, upper_arm, forearm, thigh, lower_leg, torso_side, torso_front.warp_mode-rotate_paste(quick) vsperspective_quad(real warp).blend_mode+opacity-multiplyfor dark tattoos,screenfor glowing circuitry,normalfor solid logos.
The optional inputs are where the flexibility hides: user_region_mask skips pose entirely and derives the placement region from any mask you feed (clothing, props, one-off edits - this is the manual path), and clip_mask, depth_map, and displacement_map add refinement. Outputs are image (the composite), mask (the final decal alpha), and debug_image.
Installing
Through ComfyUI Manager, search the pack title "ComfyUI Body Decal" and install. Or do it by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/verias/comfyui-body-decal.git
Restart ComfyUI. Dependencies install themselves from requirements.txt - it's just numpy and Pillow, no heavy stack, and there are no model files to download. The optional bits (DWPose, Depth Anything) come from other packs you'll already have for ControlNet work.
Gotchas
If your decal renders but shows up as a black box, your source image has no alpha and you're on use_image_alpha - switch to black_to_alpha or white_to_alpha. If nothing renders at all, check the debug view: either no person was detected, or you left alpha_mode on none. And remember displacement only does something when you actually feed a displacement_map; the sliders can be turned up and nothing will happen otherwise.
Inputs (44)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| decal | IMAGE | — | |
| section_placement | STRING | — Placement / Body Region — | — |
| placement_region | COMBO | 8 options: nose, neck_side, upper_arm, forearm, thigh, lower_leg, +2 | |
| placement_side | COMBO | 2 options: viewer_left, viewer_right | |
| placement_anchor | FLOAT | 0.200–1 | — |
| placement_along_axis | FLOAT | 0.00-2–2 | — |
| placement_across_axis | FLOAT | 0.00-2–2 | — |
| section_region | STRING | — Region Mask — | — |
| region_start_extend | FLOAT | 0.00-1–1 | — |
| region_end_extend | FLOAT | 0.35-1–2 | — |
| region_inner_width | FLOAT | 0.100–1 | — |
| region_outer_width | FLOAT | 0.220–1.5 | — |
| region_feather | INT | 100–80 | — |
| section_decal | STRING | — Decal Size / Rotation — | — |
| decal_scale | FLOAT | 1.000.2–3 | — |
| decal_height | FLOAT | 1.050.2–4 | — |
| decal_width | FLOAT | 1.000.2–4 | — |
| decal_base_rotation | FLOAT | 0-180–180 | — |
| decal_rotation_offset | FLOAT | 0.0-90–90 | — |
| manual_offset_x | FLOAT | 0-500–500 | — |
| manual_offset_y | FLOAT | 0-500–500 | — |
| warp_mode | COMBO | 2 options: rotate_paste, perspective_quad | |
| section_alpha | STRING | — Alpha / Blend — | — |
| blend_mode | COMBO | 5 options: screen, add, normal, multiply, overlay | |
| opacity | FLOAT | 0.750–1 | — |
| alpha_mode | COMBO | 5 options: use_image_alpha, black_to_alpha, white_to_alpha, luma_to_alpha, none | |
| alpha_threshold | FLOAT | 0.030–1 | — |
| alpha_softness | FLOAT | 0.100.001–1 | — |
| section_displacement | STRING | — Displacement Warp — | — |
| displace_strength_x | FLOAT | 0.0-100–100 | — |
| displace_strength_y | FLOAT | 8.0-100–100 | — |
| displace_neutral | FLOAT | 0.500–1 | — |
| depth_strength_x | FLOAT | 0.0-100–100 | — |
| depth_strength_y | FLOAT | 0.0-100–100 | — |
| depth_neutral | FLOAT | 0.500–1 | — |
| section_debug | STRING | — Debug — | — |
| debug | BOOLEAN | true | — |
| pose_keypointopt | POSE_KEYPOINT | — | |
| user_region_maskopt | MASK | — | |
| clip_maskopt | MASK | — | |
| depth_mapopt | IMAGE | — | |
| displacement_mapopt | IMAGE | — | |
| decal_alpha_maskopt | MASK | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |
| debug_image | IMAGE | — |