Compositor nodes, post-processing, and color grading in Blender. Use for compositing workflows, render pass integration, post-processing, color correction, or when user mentions "compositor," "post-process," "color grade," or "render passes."
Scanned 9/3/2026
Install to Claude Code
npx -y skills add sideshowroberto/vfx-agent-toolkit --skill blender-compositing --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Blender Compositing?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/sideshowroberto-blender-compositing)More formats (shields.io, HTML) on the badges page.
---
name: blender-compositing
description: Compositor nodes, post-processing, and color grading in Blender. Use for compositing workflows, render pass integration, post-processing, color correction, or when user mentions "compositor," "post-process," "color grade," or "render passes."
allowed-tools: Read,Write
---
# Blender Compositing Skill
**Version:** 3.0.0
**Last Updated:** 2026-06-15
**Dependencies:** Blender 5.1+
---
## Breaking Changes (5.1+)
### Compositor Node Tree Access Changed
`scene.node_tree` no longer exists. Use `scene.compositing_node_group` instead. Must create the node group manually if None.
```python
import bpy
scene = bpy.context.scene
scene.use_nodes = True
# [FAIL] BROKEN in 5.1+
# compositor = scene.node_tree
# [OK] Access or create compositor node group
comp_tree = scene.compositing_node_group
if comp_tree is None:
comp_tree = bpy.data.node_groups.new(name="Compositor", type='CompositorNodeTree')
scene.compositing_node_group = comp_tree
scene.render.use_compositing = True
```
### CompositorNodeComposite Removed
There is no Composite output node in 5.1+. The main render output comes directly from the render pipeline (set via `scene.render.filepath` and `scene.render.image_settings`). Use `CompositorNodeViewer` for preview and `CompositorNodeOutputFile` for additional outputs.
```python
import bpy
# [FAIL] BROKEN in 5.1+ (RuntimeError: Node type undefined)
# composite = nodes.new('CompositorNodeComposite')
# [OK] Main render output is controlled by scene.render settings
scene = bpy.context.scene
scene.render.filepath = "//output/render_"
scene.render.image_settings.file_format = 'PNG'
# [OK] Use Viewer for compositor preview
viewer = comp_tree.nodes.new('CompositorNodeViewer')
```
### CompositorNodeMapRange Removed
No Map Range node in the compositor. Use `ShaderNodeMath` (works in compositor context) to build equivalent logic.
```python
import bpy
# [FAIL] BROKEN in 5.1+
# map_range = comp_tree.nodes.new('CompositorNodeMapRange')
# [OK] Use Math nodes for range mapping
# Example: clamp depth to max 80m, then normalize
clamp = comp_tree.nodes.new('ShaderNodeMath')
clamp.operation = 'MINIMUM'
clamp.inputs[1].default_value = 80.0
normalize = comp_tree.nodes.new('CompositorNodeNormalize')
comp_tree.links.new(rl.outputs['Depth'], clamp.inputs[0])
comp_tree.links.new(clamp.outputs[0], normalize.inputs[0])
```
### CompositorNodeColorRamp Removed
```python
import bpy
# [FAIL] BROKEN in 4.5.0+
# color_ramp = nodes.new('CompositorNodeColorRamp')
# [OK] Use ShaderNodeValToRGB (identical API, works in compositor)
color_ramp = comp_tree.nodes.new('ShaderNodeValToRGB')
```
### OPEN_EXR_MULTILAYER Not Available as Main Render Format
Multilayer EXR can only be created via the compositor's File Output node (which defaults to `OPEN_EXR_MULTILAYER`). The main render output supports `OPEN_EXR` (single layer) but not multilayer.
```python
import bpy
# [FAIL] BROKEN in 5.1+ - not in scene render format enum
# scene.render.image_settings.file_format = 'OPEN_EXR_MULTILAYER'
# [OK] Use File Output compositor node (defaults to OPEN_EXR_MULTILAYER)
fo = comp_tree.nodes.new('CompositorNodeOutputFile')
print(fo.format.file_format) # 'OPEN_EXR_MULTILAYER'
```
### EEVEE Bloom -> Compositor Glare (4.5.3+)
```python
import bpy
# [FAIL] BROKEN
# scene.eevee.use_bloom = True
# [OK] Use Compositor Glare node
glare = comp_tree.nodes.new('CompositorNodeGlare')
glare.glare_type = 'FOG_GLOW'
glare.threshold = 0.8
glare.size = 8
```
---
## QUICK START
### Basic Compositor Setup (5.1+)
```python
import bpy
scene = bpy.context.scene
scene.use_nodes = True
# Create or get compositor node group
comp_tree = scene.compositing_node_group
if comp_tree is None:
comp_tree = bpy.data.node_groups.new(name="Compositor", type='CompositorNodeTree')
scene.compositing_node_group = comp_tree
scene.render.use_compositing = True
comp_tree.nodes.clear()
# Core nodes
rl = comp_tree.nodes.new('CompositorNodeRLayers')
rl.location = (0, 0)
viewer = comp_tree.nodes.new('CompositorNodeViewer')
viewer.location = (400, 0)
comp_tree.links.new(rl.outputs['Image'], viewer.inputs['Image'])
```
---
## STANDARD WORKFLOWS
### Workflow 1: File Output Node (Multilayer EXR)
The File Output node in 5.1+ uses `directory` and `file_name` (not `base_path`/`file_slots`). Add pass inputs via `file_output_items.new()`.
**CRITICAL: Trailing dot in `file_name`** - the File Output node appends the scene frame number to `file_name`. Without a trailing `.`, sequential numbering like `0001` gets the scene frame appended (e.g., `00010276`). Add `.` to terminate: `"output_0001."` -> `output_0001.exr`.
```python
import bpy
comp_tree = bpy.context.scene.compositing_node_group
rl = comp_tree.nodes.get('Render Layers')
fo = comp_tree.nodes.new('CompositorNodeOutputFile')
fo.location = (600, -300)
# Set output directory and base filename
fo.directory = "D:\\output\\exr\\"
fo.file_name = "shot_0001." # Trailing dot prevents frame number append
# Add pass inputs (the node starts with 1 default input + 0 file_output_items)
# Must add items for the node to actually write output
combined = fo.file_output_items.new(socket_type='RGBA', name='Combined')
depth = fo.file_output_items.new(socket_type='FLOAT', name='Depth')
crypto0 = fo.file_output_items.new(socket_type='RGBA', name='CryptoObject00')
# Connect render layer outputs to file output inputs
comp_tree.links.new(rl.outputs['Image'], fo.inputs['Combined'])
comp_tree.links.new(rl.outputs['Depth'], fo.inputs['Depth'])
comp_tree.links.new(rl.outputs['CryptoObject00'], fo.inputs['CryptoObject00'])
```
**File Output API Reference (5.1+):**
| Old API (pre-5.1) | New API (5.1+) |
|---|---|
| `fo.base_path` | `fo.directory` |
| `fo.file_slots[0].path` | `fo.file_name` |
| `fo.file_slots.new("name")` | `fo.file_output_items.new(socket_type='RGBA', name='name')` |
| Format changeable per-node | Format locked to `OPEN_EXR_MULTILAYER` |
**Socket types for `file_output_items.new()`:** `'FLOAT'`, `'RGBA'`, `'VECTOR'`, `'INT'`, `'BOOLEAN'`
---
### Workflow 2: Depth Pass Normalization
**IMPORTANT: Never change camera `clip_end` to control depth range.** Reducing `clip_end` clips geometry from the render entirely - far objects disappear. Instead, clamp depth values in the compositor.
```python
import bpy
comp_tree = bpy.context.scene.compositing_node_group
rl = comp_tree.nodes.get('Render Layers')
# Clamp depth at desired max distance (e.g., 80m)
# Objects beyond this distance map to black, but still RENDER
clamp = comp_tree.nodes.new('ShaderNodeMath')
clamp.operation = 'MINIMUM'
clamp.inputs[1].default_value = 80.0 # max depth in meters
clamp.location = (200, -200)
# Normalize to 0-1 range
normalize = comp_tree.nodes.new('CompositorNodeNormalize')
normalize.location = (400, -200)
# Invert so near=white, far=black (ControlNet convention)
invert = comp_tree.nodes.new('CompositorNodeInvert')
invert.location = (600, -200)
# Chain: Depth -> Clamp -> Normalize -> Invert -> Viewer
comp_tree.links.new(rl.outputs['Depth'], clamp.inputs[0])
comp_tree.links.new(clamp.outputs[0], normalize.inputs[0])
comp_tree.links.new(normalize.outputs[0], invert.inputs['Color'])
# View in Viewer, save via viewer_img.save_render()
viewer = comp_tree.nodes.new('CompositorNodeViewer')
viewer.location = (800, -200)
comp_tree.links.new(invert.outputs['Color'], viewer.inputs['Image'])
```
**Saving Viewer output as PNG:**
```python
import bpy
viewer_img = bpy.data.images.get('Viewer Node')
if viewer_img and viewer_img.size[0] > 0:
viewer_img.save_render(filepath="//depth_0001.png", scene=bpy.context.scene)
```
---
### Workflow 3: Color Correction Pipeline
```python
import bpy
scene = bpy.context.scene
scene.use_nodes = True
comp_tree = scene.compositing_node_group
if comp_tree is None:
comp_tree = bpy.data.node_groups.new(name="Compositor", type='CompositorNodeTree')
scene.compositing_node_group = comp_tree
scene.render.use_compositing = True
comp_tree.nodes.clear()
rl = comp_tree.nodes.new('CompositorNodeRLayers')
rl.location = (0, 0)
bright_contrast = comp_tree.nodes.new('CompositorNodeBrightContrast')
bright_contrast.location = (250, 0)
bright_contrast.inputs['Bright'].default_value = 0.1
bright_contrast.inputs['Contrast'].default_value = 0.2
rgb_curves = comp_tree.nodes.new('CompositorNodeCurveRGB')
rgb_curves.location = (500, 0)
hue_sat = comp_tree.nodes.new('CompositorNodeHueSat')
hue_sat.location = (750, 0)
hue_sat.inputs['Saturation'].default_value = 1.2
viewer = comp_tree.nodes.new('CompositorNodeViewer')
viewer.location = (1000, 0)
comp_tree.links.new(rl.outputs['Image'], bright_contrast.inputs['Image'])
comp_tree.links.new(bright_contrast.outputs['Image'], rgb_curves.inputs['Image'])
comp_tree.links.new(rgb_curves.outputs['Image'], hue_sat.inputs['Image'])
comp_tree.links.new(hue_sat.outputs['Image'], viewer.inputs['Image'])
```
---
### Workflow 4: Cryptomatte + Render Passes
```python
import bpy
scene = bpy.context.scene
vl = scene.view_layers["ViewLayer"]
# Enable passes
vl.use_pass_combined = True
vl.use_pass_z = True
vl.use_pass_cryptomatte_object = True
vl.use_pass_cryptomatte_accurate = True
# Cryptomatte outputs appear as CryptoObject00, CryptoObject01, CryptoObject02
# on the Render Layers node. Must be saved in multilayer EXR to be useful
# (use File Output node - see Workflow 1)
```
---
## TROUBLESHOOTING
### "Scene object has no attribute node_tree"
**Cause:** Blender 5.1+ removed `scene.node_tree`
**Fix:** Use `scene.compositing_node_group` (see Breaking Changes above)
### "Node type CompositorNodeComposite undefined"
**Cause:** Composite node removed in 5.1+
**Fix:** Main output uses `scene.render.filepath`. Use Viewer for preview.
### File Output Node Produces No Files
**Cause:** `file_output_items` is empty (0 items). The default input exists but without items defined, nothing is written.
**Fix:** Add at least one item via `fo.file_output_items.new(socket_type='RGBA', name='Combined')`
### File Output Frame Numbers Wrong (e.g., 00010276)
**Cause:** File Output appends the scene frame number to `file_name`
**Fix:** Add trailing `.` to `file_name`: `fo.file_name = "output_0001."`
### Far Objects Missing from Render
**Cause:** Camera `clip_end` was reduced for depth normalization
**Fix:** Never change `clip_end`. Use compositor Math(MINIMUM) node to clamp depth values instead.
### Render Passes Not Showing in Render Layers Node
```python
import bpy
view_layer = bpy.context.scene.view_layers[0]
view_layer.use_pass_z = True
view_layer.use_pass_normal = True
```
---
## VALIDATION CHECKLIST
- [ ] Compositor accessed via `scene.compositing_node_group` (not `scene.node_tree`)
- [ ] No `CompositorNodeComposite` nodes (removed in 5.1)
- [ ] No `CompositorNodeMapRange` nodes (removed - use ShaderNodeMath)
- [ ] ColorRamp nodes use `ShaderNodeValToRGB`
- [ ] File Output uses `directory`/`file_name` (not `base_path`/`file_slots`)
- [ ] File Output `file_name` ends with `.` when using custom frame numbering
- [ ] File Output has `file_output_items` defined (otherwise produces no output)
- [ ] Depth normalization uses compositor clamp, NOT camera `clip_end`
- [ ] Render passes enabled in view layer before use
- [ ] `scene.render.use_compositing = True` set
---
## VERSION HISTORY
**v3.0.0** (2026-06-15) - Blender 5.1.2 compositor overhaul
- `scene.node_tree` -> `scene.compositing_node_group` (CompositorNodeTree)
- `CompositorNodeComposite` removed - documented replacement pattern
- `CompositorNodeMapRange` removed - Math node workaround
- File Output node: `base_path`->`directory`, `file_slots`->`file_output_items`
- File Output trailing dot naming fix
- OPEN_EXR_MULTILAYER only via File Output node
- Depth normalization: never change clip_end, use compositor clamp
- Added Cryptomatte + render pass workflow
**v2.0.0** (2026-06-10) - MCP migration
**v1.1.0** (2025-10-24) - Progressive disclosure update
**v1.0.0** (2025-10-24) - Initial release
---
**Status:** Production Ready
**Maintainer:** VFX Pipeline Team
**Dependencies:** Blender 5.1+
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!