Layer Effect: Inner Shadow (MEC)
Inner shadow, and why your contact shadow keeps haloing
- layer_image
- background_image
- layer_mask
- image
- effect_mask
- report
What it's for
The inner shadow is the "this thing sits inside something / this thing has depth" effect: a thin dark band just inside the matte edge, offset in one direction. It's how you fake a recessed panel, a hole, a patch that's stitched into fabric, or the sense that a subject is lit from one side while its edge falls off.
It's the sibling of Drop Shadow and shares the same code path - literally: in the source, InnerShadow is a subclass of DropShadow. The difference is where the footprint lands.
How it works
The matte is shifted by distance_x/distance_y, grown by grow, blurred by blur - same as the drop shadow. But instead of compositing the shadow outside the subject, the node takes the intersection of the shadow band with the layer matte. So you only get the part of the shifted, blurred shadow that falls inside the matte, which is exactly the "inner" half of the effect.
effect_mask is that intersection: the shadow band, clipped to the layer. Handy if you want to grade or grain the inner shadow separately, or feed it somewhere as a matte in its own right.
Inputs
- layer_image - the subject. Alpha counts if you don't wire a mask.
- layer_mask (optional) - the matte. One of the two is required; a 3-channel image with nothing wired raises
needs a layer mask. - invert_mask - defaults to true and inverts for real. Normal ComfyUI matte plus the default = the layer is treated as the background, which puts your inner shadow in the wrong region entirely. This is the first thing to check when the effect is somewhere unexpected.
- distance_x / distance_y - positive moves right and down, same convention as the drop shadow, and the opposite of upstream LayerStyle's sign. Defaults are 5/5, which is a subtle light-from-upper-left look.
- grow - default 2. Small numbers here; you're shaping a band a few pixels wide, not inflating a silhouette.
- blur - default 15. The blur is what makes it read as falloff instead of a drawn line.
- shadow_color, blend_mode (30 modes), opacity (default 50), dissolve_seed - the dissolve mode is seeded and static here, unlike upstream, which reseeded every frame and made dissolves flicker across a sequence.
Optional background_image is the plate; without it you composite onto a transparent canvas (read: black, in the RGB output).
Outputs: image, effect_mask, report.
The halo trap
Here's the thing nobody explains about contact shadows: if the shadow band extends to the edge of the matte and then stops, you get a visible rim - the shadow appears to leak past the object silhouette. Two causes, two fixes:
growpositive with a bigblur. The blur spreads the band outward, past the matte, and the intersection with the matte cuts it off mid-falloff, leaving a hard line. Pullgrownegative - the tooltip's own phrasing is that a negative grow "keeps a tight contact shadow from haloing", which is precisely this problem.- A matte that's a pixel or two tight. Inner shadows are unforgiving of a matte that's undersized, because the band starts at the boundary. Expand the matte a hair upstream and the shadow reads as being in the material rather than painted on.
For a believable recessed-panel look: grow around -1 to 0, blur 8–12, opacity 40–60, distance 3–6 px, and a shadow colour that's a dark tint of the surface, not black.
Install
ComfyUI Manager → search "CustomNodePacks", or:
cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks
Restart and check the console load line. This port is standalone - it does not need chflame163's ComfyUI_LayerStyle installed, which is the point of shipping a torch-native port rather than another wrapper (upstream is a few hundred nodes with a dependency list to match, plus API-calling nodes). Tier 1, no models. The README's warning about not blindly running pip install -r requirements.txt applies; check pip list first.
Common issues
- Effect lands outside the subject.
invert_maskis still on its defaulttrue. Flip it. - A hard bright rim at the matte boundary. The halo trap above: negative
grow, or a slightly padded matte. - Nothing visible at all. At defaults this is a 5-pixel offset with a 15-pixel blur on a 50% opacity multiply - genuinely subtle on a small subject. Scale the distance with the subject size; 5px is a lot on a 256px thumbnail and nothing on a 2048px plate.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| layer_image | IMAGE | Foreground layer whose alpha defines the cast shadow. | |
| invert_mask | BOOLEAN | true | Flip mask polarity when your matte is white-on-black. |
| blend_mode | COMBO | 30 options: normal, dissolve, darken, multiply, color burn, linear burn, +24 | |
| opacity | INT | 500–100 | — |
| distance_x | INT | 5-9999–9999 | Horizontal offset in pixels. Positive moves the shadow right — the opposite of the upstream LayerStyle node, which negated it. |
| distance_y | INT | 5-9999–9999 | Vertical offset in pixels. Positive moves the shadow down — the opposite of the upstream LayerStyle node, which negated it. |
| grow | INT | 2-9999–9999 | Expand the shadow before blurring. Positive spreads it past the subject edge; negative pulls it inside, which keeps a tight contact shadow from haloing. |
| blur | INT | 150–1000 | Gaussian blur radius on the shadow matte. |
| shadow_color | STRING | #000000 | — |
| dissolve_seed | INT | 00–2147483647 | Only used by the 'dissolve' blend mode. Upstream reseeds every frame, so a dissolve flickers through a sequence; a fixed seed here holds still. |
| background_imageopt | IMAGE | Plate; empty = transparent canvas (V3 union). | |
| layer_maskopt | MASK | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| effect_mask | MASK | — |
| report | STRING | — |