ReLight π‘
Fake a three-point lighting rig without regenerating a single pixel
- image
- mask
- image
- mask
- debug_image
There are two different things called "relighting" in this ecosystem, and keeping them straight is half the battle. IC-Light and the Qwen/Flux edit models are diffusion relighters - they re-emit the whole frame, shadows and all, and can hallucinate a logo or alter a texture while they're at it. ReLight is the other kind. It's a single ComfyUI node that paints up to three positionable light sources onto an image with pure math: numpy, Pillow, scipy, zero diffusion passes, zero models to download, deterministic every run. If you want a subject to catch a warm window glow without ever asking a denoiser to re-invent it, this is the node.
That determinism is the whole pitch. Diffusion relighters can't promise a consistent product catalogue; ReLight only promises that, because it modifies illumination and leaves the pixels' structure alone by construction. It's also near-instant, so it's what you reach for when you want to try twenty lighting ideas on a hero image before committing to a generation, or when you want to feed a deliberately lit result into ControlNet. It won't give you physically-correct global illumination or move reflections - it paints light onto what's already there, it doesn't re-render the scene. Know the limit and you won't be disappointed.
How it works
For each light source the node builds a mask centered on light_position_x/light_position_y - a radial falloff that's full strength inside inner_circle_radius and fades linearly to zero at outer_circle_radius, or a directional gradient if you flip on use_gradient_mode (that's your sunset-rays and window-light look). Everything gets softened by mask_blur.
Then it picks a lane. In the default color-correction mode it applies brightness/contrast/saturation/temperature/tint/gamma to the inner zone and a separate set to the outer ring - the v2.1.0 changelog fixed the outer values actually applying, so presets like "Spotlight" behave as designed now. With colored lights on, it does plain additive light: image + color Γ intensity Γ mask, clamped to a valid range.
The "3D" part is where the optional mask input earns its keep. The node runs a Sobel edge detection over your foreground mask, then modulates the light by how its direction lines up with the edge normals - that's how it fakes occlusion. Behind Subject leaves a rim of light around the silhouette (boost with rim_amplification); In Front of Subject lights the subject's face. No ray tracing, but for a static image it reads surprisingly well.
The inputs and outputs that matter
You'll set five things, honestly:
- image - the only required input.
- mask - white = subject, black = background. Needed for the occlusion directions and for
remove_background. - preset - 8 ready-made looks. One gotcha: a preset overrides the widgets on the node, so the values you see may be ignored for whatever the preset defines.
preserve_positioningkeeps your light positions and radii when you pick one. - num_light_sources (1β3) - but note: lights 2 and 3 get their own position, radius and color while reusing Light 1's correction settings in color-correction mode. Set Light 1's first.
- effect_strength - master intensity. If you see nothing, raise this first.
Outputs are three: image (the relit result, which is what you wire into a Save/Preview or into ControlNet), mask (pass-through of your input, black if none connected), and debug_image - a visualization of light positions and masks, produced when show_debug_info is on.
Install
ComfyUI Manager: search "ReLight" and install, then restart. Or the old-fashioned way:
cd path/to/ComfyUI/custom_nodes
git clone https://github.com/EnragedAntelope/comfyui-relight
pip install -r comfyui-relight/requirements.txt
That's it - numpy, Pillow, scipy; torch comes from ComfyUI itself. There are no model files to download. One version catch worth knowing: the node is built on the ComfyUI v3 node schema, which needs ComfyUI 0.3.48+. If the node never shows up after install, update ComfyUI first, not the node. The README also recommends ComfyUI Essentials for proper masks (its RemBG node gives you the clean foreground mask this thing wants).
Common issues
- Subject looks unlit, or the effect is backwards. Your mask is inverted. ReLight expects white = subject; invert it upstream with an InvertMask node.
- No visible effect. Raise
effect_strengthor the per-lightlight_intensity. - Occlusion isn't doing anything.
apply_3d_lightingmust be on and a mask connected; without one it silently degrades to flat lighting. - Node fails to load.
scipyisn't installed -pip install scipyand restart. - Black debug image. Check the ComfyUI console; the debug output only renders for the first image in a batch.
A quick workflow to try: load an image, run RemBG for a mask, drop ReLight in with "Warm Sunset Glow" behind the subject, and preview alongside the debug image - the sample workflow in the repo does exactly this. It's the fastest way to see whether the "fake it with math" school of relighting is for you.
Inputs (50)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | The input image to apply lighting effects to | |
| preset | COMBO | None | Select a preset or 'None' for custom settings. NOTE: a preset overrides the widgets below - the values shown on the node are ignored for whatever the preset defines. The one exception is 'effect_strength', which scales the preset instead of being replaced by it |
| num_light_sources | INT | 11β3 | Number of light sources (1-3). Lights 2 and 3 have their own position, radius and color, but in color-correction mode they reuse Light 1's correction settings |
| preserve_positioning | BOOLEAN | false | Keep your own light positions and radii when a preset is selected, instead of letting the preset set them |
| show_debug_info | BOOLEAN | false | Output a debug visualization image (first image of the batch; zone overlays show Light 1, position and radius indicators are drawn for every light) |
| use_colored_lights | BOOLEAN | false | Use additive colored light instead of color correction? |
| use_gradient_mode | BOOLEAN | false | Use directional gradient masks instead of radial? |
| apply_3d_lighting | BOOLEAN | true | Master switch for occlusion. Leave on and use 'light_direction' to choose the behaviour; turning this off forces 'No Occlusion' |
| light_direction | COMBO | No Occlusion | How light interacts with the subject. 'Behind'/'In Front' require a mask |
| remove_background | BOOLEAN | false | Composite the lit result back over the untouched original using the mask, so only the subject is relit. Does not remove anything. Ignored for 'Behind Subject' and 'In Front of Subject' |
| effect_strength | FLOAT | 1.00β5 | Overall intensity multiplier for lighting adjustments/colors, gamma included. 0.0 leaves the image untouched. With a preset active this scales the preset's own strength, so 1.0 is the preset as designed. Does not scale rim_amplification or mask_blur - those have their own controls |
| mask_blur | FLOAT | 500β200 | Blur radius for light mask edges (smoother transitions) |
| rim_amplification | FLOAT | 2.00β10 | Intensity boost specifically for rim light component (when 'Behind Subject') |
| light_position_x | FLOAT | 0.500β1 | Light 1: Horizontal position (0=left, 1=right) |
| light_position_y | FLOAT | 0.500β1 | Light 1: Vertical position (0=top, 1=bottom) |
| inner_circle_radius | FLOAT | 0.400β1 | Light 1: Inner radius (strongest effect area) |
| outer_circle_radius | FLOAT | 0.700β1 | Light 1: Outer radius (falloff area) |
| light_color_r | INT | 2550β255 | Light 1: Red color (if 'Use Colored Lights' is True) |
| light_color_g | INT | 2550β255 | Light 1: Green color (if 'Use Colored Lights' is True) |
| light_color_b | INT | 2550β255 | Light 1: Blue color (if 'Use Colored Lights' is True) |
| light_intensity | FLOAT | 1.00β3 | Light 1: Intensity (if 'Use Colored Lights' is True) |
| inner_brightness | FLOAT | 10-100β100 | Light 1: Inner area brightness (Color Correction mode) |
| inner_contrast | FLOAT | 5-100β100 | Light 1: Inner area contrast (Color Correction mode) |
| inner_saturation | FLOAT | 5-100β100 | Light 1: Inner area saturation (Color Correction mode) |
| inner_temperature | FLOAT | 0-100β100 | Light 1: Inner area temperature (-100=cool, 100=warm) |
| inner_tint | FLOAT | 0-100β100 | Light 1: Inner area tint (-100=magenta, 100=green) |
| inner_gamma | FLOAT | 1.000.1β5 | Light 1: Inner area gamma. Above 1.0 brightens midtones, below 1.0 darkens them |
| outer_brightness | FLOAT | -10-100β100 | Light 1: Outer area brightness (Color Correction mode) |
| outer_contrast | FLOAT | 0-100β100 | Light 1: Outer area contrast (Color Correction mode) |
| outer_saturation | FLOAT | -10-100β100 | Light 1: Outer area saturation (Color Correction mode) |
| outer_temperature | FLOAT | 0-100β100 | Light 1: Outer area temperature |
| outer_tint | FLOAT | 0-100β100 | Light 1: Outer area tint |
| outer_gamma | FLOAT | 0.910.1β5 | Light 1: Outer area gamma. Above 1.0 brightens midtones, below 1.0 darkens them |
| maskopt | MASK | Foreground mask (white=subject, black=background). Needed for occlusion ('Behind Subject' / 'In Front of Subject') and for 'remove_background' compositing. Resized automatically if it does not match the image | |
| light2_position_xopt | FLOAT | 0.800β1 | Light 2: Horizontal position |
| light2_position_yopt | FLOAT | 0.200β1 | Light 2: Vertical position |
| light2_inner_radiusopt | FLOAT | 0.300β1 | Light 2: Inner radius |
| light2_outer_radiusopt | FLOAT | 0.600β1 | Light 2: Outer radius |
| light2_color_ropt | INT | 1800β255 | Light 2: Red color |
| light2_color_gopt | INT | 1800β255 | Light 2: Green color |
| light2_color_bopt | INT | 2550β255 | Light 2: Blue color |
| light2_intensityopt | FLOAT | 0.70β3 | Light 2: Intensity (Colored mode) |
| light3_position_xopt | FLOAT | 0.300β1 | Light 3: Horizontal position |
| light3_position_yopt | FLOAT | 0.800β1 | Light 3: Vertical position |
| light3_inner_radiusopt | FLOAT | 0.250β1 | Light 3: Inner radius |
| light3_outer_radiusopt | FLOAT | 0.500β1 | Light 3: Outer radius |
| light3_color_ropt | INT | 2550β255 | Light 3: Red color |
| light3_color_gopt | INT | 1500β255 | Light 3: Green color |
| light3_color_bopt | INT | 1200β255 | Light 3: Blue color |
| light3_intensityopt | FLOAT | 0.50β3 | Light 3: Intensity (Colored mode) |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The relit image |
| mask | MASK | Pass-through of the input mask, normalised to (batch, height, width) and resized to the image (black if none connected) |
| debug_image | IMAGE | Visualization of light positions and masks (enable 'show_debug_info') |