Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Automate Blender compositing and post-processing with Python. Use when the user wants to set up compositor nodes, add post-processing effects, color correct renders, combine render passes, apply blur or glare, key green screens, create node-based VFX pipelines, or script the Blender compositor.
.claude/skills/terminalskills-blender-compositing/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 98% | 58 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 172% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 15% | 0% |
| case-13 | ✓→✓ | = Same ✓ | 56% | 0% |
| case-19 | ✓→✓ | = Same ✓ | 115% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 138% | 0% |
Build node-based compositing pipelines in Blender using Python. Set up render passes, add post-processing effects (blur, glare, color correction), combine layers, key green screens, and output final composited images — all scriptable from the terminal.
pythonimport bpy scene = bpy.context.scene scene.use_nodes = True tree = scene.node_tree nodes = tree.nodes links = tree.links nodes.clear() # Minimum setup: Render Layers → Composite rl_node = nodes.new('CompositorNodeRLayers') rl_node.location = (0, 0) comp_node = nodes.new('CompositorNodeComposite') comp_node.location = (600, 0) links.new(rl_node.outputs['Image'], comp_node.inputs['Image']) # Optional viewer for preview viewer = nodes.new('CompositorNodeViewer') viewer.location = (600, -200) links.new(rl_node.outputs['Image'], viewer.inputs['Image'])
python# Brightness/Contrast bc = nodes.new('CompositorNodeBrightContrast') bc.inputs['Bright'].default_value = 10 bc.inputs['Contrast'].default_value = 20 # Hue/Saturation/Value hsv = nodes.new('CompositorNodeHueSat') hsv.inputs['Hue'].default_value = 0.5 # 0.5 = no change hsv.inputs['Saturation'].default_value = 1.2 # Color Balance (Lift/Gamma/Gain) cb = nodes.new('CompositorNodeColorBalance') cb.correction_method = 'LIFT_GAMMA_GAIN' cb.lift = (0.95, 0.95, 1.0) # cool shadows cb.gain = (1.1, 1.05, 1.0) # warm highlights # Gamma gamma = nodes.new('CompositorNodeGamma') gamma.inputs['Gamma'].default_value = 1.2
python# Blur blur = nodes.new('CompositorNodeBlur') blur.filter_type = 'GAUSS' blur.size_x = 10 blur.size_y = 10 # Glare (bloom, streaks, fog glow) glare = nodes.new('CompositorNodeGlare') glare.glare_type = 'BLOOM' # BLOOM, STREAKS, FOG_GLOW, SIMPLE_STAR, GHOSTS glare.threshold = 0.8 glare.size = 6 glare.quality = 'HIGH' # Denoise (best placed right after Render Layers) denoise = nodes.new('CompositorNodeDenoise') # Sharpen sharpen = nodes.new('CompositorNodeFilter') sharpen.filter_type = 'SHARPEN'
python# Enable render passes on the view layer view_layer = bpy.context.view_layer view_layer.use_pass_diffuse_color = True view_layer.use_pass_glossy_direct = True view_layer.use_pass_ambient_occlusion = True view_layer.use_pass_z = True # Mix passes: Diffuse * AO rl = nodes.new('CompositorNodeRLayers') mix = nodes.new('CompositorNodeMixRGB') mix.blend_type = 'MULTIPLY' mix.inputs['Fac'].default_value = 0.5 links.new(rl.outputs['DiffCol'], mix.inputs[1]) links.new(rl.outputs['AO'], mix.inputs[2])
python# Alpha Over — composite foreground over background alpha_over = nodes.new('CompositorNodeAlphaOver') alpha_over.inputs['Fac'].default_value = 1.0 # Connect: background → input 1, foreground (with alpha) → input 2 # Keying node — green screen removal keying = nodes.new('CompositorNodeKeying') keying.inputs['Key Color'].default_value = (0, 1, 0, 1) keying.clip_black = 0.1 keying.clip_white = 0.9 # Color Spill — remove green fringing after keying spill = nodes.new('CompositorNodeColorSpill') spill.channel = 'G' spill.limit_method = 'AVERAGE'
pythonfile_out = nodes.new('CompositorNodeOutputFile') file_out.base_path = "/tmp/comp_output/" file_out.format.file_format = 'OPEN_EXR_MULTILAYER' file_out.file_slots.new("Diffuse") file_out.file_slots.new("AO") rl = nodes.get('Render Layers') links.new(rl.outputs['DiffCol'], file_out.inputs['Diffuse']) links.new(rl.outputs['AO'], file_out.inputs['AO'])
User request: "Add a cinematic look — warm highlights, cool shadows, bloom, and vignette"
pythonimport bpy scene = bpy.context.scene scene.use_nodes = True tree = scene.node_tree nodes = tree.nodes links = tree.links nodes.clear() rl = nodes.new('CompositorNodeRLayers') rl.location = (0, 0) # Color Balance cb = nodes.new('CompositorNodeColorBalance') cb.location = (250, 0) cb.correction_method = 'LIFT_GAMMA_GAIN' cb.lift = (0.92, 0.93, 1.0) cb.gain = (1.15, 1.08, 0.95) links.new(rl.outputs['Image'], cb.inputs['Image']) # Bloom glare = nodes.new('CompositorNodeGlare') glare.location = (500, 0) glare.glare_type = 'BLOOM' glare.threshold = 0.7 glare.size = 7 links.new(cb.outputs['Image'], glare.inputs['Image']) # Vignette (Ellipse Mask → Blur → Multiply) ellipse = nodes.new('CompositorNodeEllipseMask') ellipse.location = (300, -400) ellipse.width = 0.85 ellipse.height = 0.85 blur_mask = nodes.new('CompositorNodeBlur') blur_mask.location = (500, -400) blur_mask.filter_type = 'GAUSS' blur_mask.size_x = 200 blur_mask.size_y = 200 links.new(ellipse.outputs['Mask'], blur_mask.inputs['Image']) mix_vig = nodes.new('CompositorNodeMixRGB') mix_vig.location = (750, 0) mix_vig.blend_type = 'MULTIPLY' mix_vig.inputs['Fac'].default_value = 0.6 links.new(glare.outputs['Image'], mix_vig.inputs[1]) links.new(blur_mask.outputs['Image'], mix_vig.inputs[2]) comp = nodes.new('CompositorNodeComposite') comp.location = (1000, 0) links.new(mix_vig.outputs['Image'], comp.inputs['Image'])
User request: "Composite my 3D character over a photo background"
pythonimport bpy scene = bpy.context.scene scene.use_nodes = True scene.render.film_transparent = True # Use alpha instead of keying tree = scene.node_tree nodes = tree.nodes links = tree.links nodes.clear() rl = nodes.new('CompositorNodeRLayers') rl.location = (0, 0) bg_image = nodes.new('CompositorNodeImage') bg_image.location = (0, -400) bg_image.image = bpy.data.images.load("/path/to/background.jpg") alpha_over = nodes.new('CompositorNodeAlphaOver') alpha_over.location = (400, -100) links.new(bg_image.outputs['Image'], alpha_over.inputs[1]) links.new(rl.outputs['Image'], alpha_over.inputs[2]) comp = nodes.new('CompositorNodeComposite') comp.location = (650, -100) links.new(alpha_over.outputs['Image'], comp.inputs['Image'])
scene.use_nodes = True before accessing the compositor node tree.film_transparent = True with Alpha Over instead of Keying when you control the 3D scene.OPEN_EXR_MULTILAYER on File Output to save all passes in one file.node.location) are for visual layout only — they don't affect functionality.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-13 | pass→pass | 10,626 | 6,272 | -41% | 1 | 1 | 0% | 2,408 | 3,752 | +56% | 0 | 0 | — |
case-19 | pass→pass | 8,455 | 6,149 | -27% | 1 | 1 | 0% | 1,552 | 3,342 | +115% | 0 | 0 | — |
case-01 | pass→pass | 7,057 | 4,516 | -36% | 1 | 1 | 0% | 1,333 | 3,178 | +138% | 0 | 0 | — |
case-02 | pass→pass | 7,413 | 4,087 | -45% | 1 | 1 | 0% | 1,390 | 3,166 | +128% | 0 | 0 | — |
case-03 | pass→pass | 8,755 | 5,837 | -33% | 1 | 1 | 0% | 1,818 | 3,553 | +95% | 0 | 0 | — |
case-04 | pass→pass | 4,738 | 3,506 | -26% | 1 | 1 | 0% | 983 | 3,055 | +211% | 0 | 0 | — |
case-05 | pass→pass | 6,322 | 4,273 | -32% | 1 | 1 | 0% | 1,206 | 3,218 | +167% | 0 | 0 | — |
case-06 | pass→pass | 4,339 | 4,152 | -4% | 1 | 1 | 0% | 1,122 | 3,248 | +189% | 0 | 0 | — |
case-07 | pass→pass | 5,048 | 4,419 | -12% | 1 | 1 | 0% | 1,001 | 3,224 | +222% | 0 | 0 | — |
case-08 | pass→pass | 11,288 | 5,397 | -52% | 1 | 1 | 0% | 2,371 | 3,239 | +37% | 0 | 0 | — |
case-09 | pass→pass | 16,660 | 8,376 | -50% | 1 | 1 | 0% | 2,969 | 4,022 | +35% | 0 | 0 | — |
case-10 | fail→pass | 5,547 | 3,291 | -41% | 1 | 1 | 0% | 1,108 | 3,009 | +172% | 0 | 0 | — |
case-11 | pass→pass | 7,471 | 8,957 | +20% | 1 | 1 | 0% | 1,566 | 4,467 | +185% | 0 | 0 | — |
case-12 | pass→pass | 8,812 | 5,974 | -32% | 1 | 1 | 0% | 1,958 | 3,747 | +91% | 0 | 0 | — |
case-14 | pass→pass | 8,167 | 25,648 | +214% | 1 | 1 | 0% | 1,676 | 3,321 | +98% | 0 | 0 | — |
case-15 | pass→pass | 6,328 | 4,669 | -26% | 1 | 1 | 0% | 1,368 | 3,371 | +146% | 0 | 0 | — |
case-16 | pass→pass | 10,738 | 6,472 | -40% | 1 | 1 | 0% | 2,360 | 3,582 | +52% | 0 | 0 | — |
case-17 | pass→pass | 12,443 | 9,246 | -26% | 1 | 1 | 0% | 2,240 | 4,245 | +90% | 0 | 0 | — |
case-18 | fail→pass | 20,435 | 13,561 | -34% | 1 | 1 | 0% | 4,379 | 5,025 | +15% | 0 | 0 | — |
case-20 | pass→pass | 9,597 | 5,208 | -46% | 1 | 1 | 0% | 2,177 | 3,462 | +59% | 0 | 0 | — |
case-21 | pass→pass | 5,538 | 4,753 | -14% | 1 | 1 | 0% | 1,121 | 3,309 | +195% | 0 | 0 | — |
case-22 | pass→pass | 7,203 | 4,342 | -40% | 1 | 1 | 0% | 1,480 | 3,226 | +118% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.