Claude Code for Blender Python (2026)

Why Claude Code for Blender Scripting

Blender’s Python API (bpy) exposes over 10,000 functions across mesh operations, materials, modifiers, constraints, and rendering. The API changes between major versions, documentation is scattered across wiki pages and source code, and common tasks like batch-processing 500 FBX files or generating procedural geometry require 100+ lines of bpy calls that are hard to discover.

Claude Code generates working bpy scripts that target your Blender version, use the correct context overrides for headless operation, and handle the operator polling that causes “context is incorrect” errors. It produces scripts for procedural modeling, automated UV unwrapping, batch export, and render farm job generation.

The Workflow

Step 1: Setup

# Blender 4.x with Python module
# macOS
brew install blender
# Verify Python access
blender --background --python-expr "import bpy; print(bpy.app.version_string)"
# For headless/CI usage
pip install fake-bpy-module-4.0  # type stubs for IDE completion
mkdir -p blender/{scripts,assets,output}

Step 2: Procedural Asset Generator

# blender/scripts/procedural_building.py
"""Generate procedural building geometry in Blender."""
import bpy
import bmesh
import math
import random
from mathutils import Vector
MAX_FLOORS = 200
MAX_WINDOWS_PER_FLOOR = 50
MIN_DIMENSION = 0.1
def clear_scene() -> None:
    """Remove all objects from the current scene."""
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete()
    assert len(bpy.data.objects) == 0, "Scene not cleared"
def create_floor_plate(width: float, depth: float,
                       height: float, floor_num: int) -> bpy.types.Object:
    """Create a single floor of the building."""
    assert width > MIN_DIMENSION, f"Width too small: {width}"
    assert depth > MIN_DIMENSION, f"Depth too small: {depth}"
    assert height > MIN_DIMENSION, f"Height too small: {height}"
    bpy.ops.mesh.primitive_cube_add(
        size=1,
        location=(0, 0, floor_num * height + height / 2))
    obj = bpy.context.active_object
    obj.name = f"Floor_{floor_num:03d}"
    obj.scale = (width, depth, height)
    # Apply scale
    bpy.ops.object.transform_apply(scale=True)
    assert obj is not None, "Floor object creation failed"
    return obj
def add_windows(floor_obj: bpy.types.Object,
                window_width: float = 1.2,
                window_height: float = 1.5,
                spacing: float = 3.0,
                inset_depth: float = 0.15) -> int:
    """Boolean-cut windows into a floor plate."""
    assert floor_obj is not None
    assert window_width > 0 and window_height > 0
    dims = floor_obj.dimensions
    n_windows_x = max(1, int((dims.x - spacing) / spacing))
    n_windows_y = max(1, int((dims.y - spacing) / spacing))
    assert n_windows_x <= MAX_WINDOWS_PER_FLOOR
    assert n_windows_y <= MAX_WINDOWS_PER_FLOOR
    window_count = 0
    floor_z = floor_obj.location.z
    # Front and back faces
    for i in range(n_windows_x):
        x_pos = -dims.x / 2 + spacing + i * spacing
        for y_sign in (-1, 1):
            y_pos = y_sign * dims.y / 2
            bpy.ops.mesh.primitive_cube_add(
                size=1,
                location=(x_pos, y_pos, floor_z))
            window = bpy.context.active_object
            window.name = f"Window_temp_{window_count}"
            window.scale = (window_width, inset_depth * 2, window_height)
            bpy.ops.object.transform_apply(scale=True)
            # Boolean difference
            mod = floor_obj.modifiers.new("Window", 'BOOLEAN')
            mod.operation = 'DIFFERENCE'
            mod.object = window
            bpy.context.view_layer.objects.active = floor_obj
            bpy.ops.object.modifier_apply(modifier="Window")
            bpy.data.objects.remove(window)
            window_count += 1
    return window_count
def create_building(num_floors: int = 10, width: float = 20.0,
                    depth: float = 12.0, floor_height: float = 3.5,
                    add_detail: bool = True) -> bpy.types.Object:
    """Generate a complete procedural building."""
    assert 1 <= num_floors <= MAX_FLOORS, \
        f"Floor count {num_floors} out of range"
    clear_scene()
    floor_objects = []
    for i in range(num_floors):
        # Slight width variation for visual interest
        w = width * (1.0 - i * 0.005)
        d = depth * (1.0 - i * 0.005)
        floor = create_floor_plate(w, d, floor_height, i)
        if add_detail and i > 0:
            add_windows(floor, spacing=3.0)
        floor_objects.append(floor)
    # Join all floors into one object
    bpy.ops.object.select_all(action='SELECT')
    bpy.context.view_layer.objects.active = floor_objects[0]
    bpy.ops.object.join()
    building = bpy.context.active_object
    building.name = "Procedural_Building"
    # Add material
    mat = bpy.data.materials.new("Building_Concrete")
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes["Principled BSDF"]
    bsdf.inputs["Base Color"].default_value = (0.65, 0.62, 0.58, 1.0)
    bsdf.inputs["Roughness"].default_value = 0.85
    building.data.materials.append(mat)
    assert building is not None, "Building creation failed"
    print(f"Created building: {num_floors} floors, "
          f"{len(building.data.vertices)} vertices")
    return building
def batch_export(directory: str, format: str = "FBX") -> None:
    """Export all objects in scene to individual files."""
    assert format in ("FBX", "OBJ", "GLTF"), f"Unknown format: {format}"
    export_funcs = {
        "FBX": lambda p: bpy.ops.export_scene.fbx(filepath=p, use_selection=True),
        "OBJ": lambda p: bpy.ops.export_scene.obj(filepath=p, use_selection=True),
        "GLTF": lambda p: bpy.ops.export_scene.gltf(filepath=p, use_selection=True),
    }
    for obj in bpy.data.objects:
        bpy.ops.object.select_all(action='DESELECT')
        obj.select_set(True)
        filepath = f"{directory}/{obj.name}.{format.lower()}"
        export_funcs[format](filepath)
        print(f"Exported: {filepath}")
if __name__ == "__main__":
    building = create_building(
        num_floors=15, width=24.0, depth=14.0,
        floor_height=3.2, add_detail=True)
    # Render
    bpy.context.scene.render.engine = 'CYCLES'
    bpy.context.scene.cycles.samples = 128
    bpy.context.scene.render.filepath = "output/building_render.png"
    bpy.ops.render.render(write_still=True)
    print("Render complete: output/building_render.png")

Step 3: Run Headless

# Run script in headless Blender
blender --background --python blender/scripts/procedural_building.py
# Expected: building_render.png in output/
# Batch processing pipeline
for file in assets/*.fbx; do
  blender --background --python scripts/process_asset.py -- "$file"
done

CLAUDE.md for Blender Scripting

# Blender Python Scripting Rules
## Standards
- Blender 4.x Python API (bpy)
- PEP 8 for script formatting
- Headless-compatible (--background flag)
## File Formats
- .py (Blender scripts)
- .blend (Blender project)
- .fbx / .gltf / .obj (exchange formats)
- .exr / .png (render output)
## Libraries
- bpy (Blender Python API)
- bmesh (mesh editing API)
- mathutils (Vector, Matrix, Quaternion)
- fake-bpy-module-4.0 (IDE type stubs)
## Testing
- Scripts must run headless (no GUI dependency)
- Verify mesh integrity: no zero-area faces, no isolated vertices
- Render output resolution must match scene settings
## Rules
- Always apply transforms before export
- Clear scene before procedural generation
- Use bmesh for complex mesh operations (faster than operators)
- Context overrides for operators that require specific context

Common Pitfalls

  • “Context is incorrect” errors: Many bpy.ops operators require specific active objects or edit modes. Claude Code wraps operator calls with proper context overrides and mode switches.

For more on this topic, see Best Claude Code Plugins for Python.

  • Memory leaks in batch processing: Blender accumulates orphan data blocks across iterations. Claude Code adds bpy.ops.outliner.orphans_purge(do_recursive=True) between batch iterations.
  • API changes between versions: bpy.context.scene.render.engine changed between 3.x and 4.x. Claude Code checks bpy.app.version and uses version-appropriate API calls.

Frequently Asked Questions

Do I need a paid Anthropic plan to use this?

Claude Code works with any Anthropic API plan, including the free tier. However, the free tier has lower rate limits (requests per minute and tokens per minute) that may slow down multi-step workflows. For professional use, the Build or Scale plan provides higher limits and priority access during peak hours.

How does this affect token usage and cost?

The token cost depends on the size of your prompts and Claude’s responses. Typical development tasks consume 10K-50K tokens per interaction. Using a CLAUDE.md file and skills reduces exploration tokens by 50-80%, which directly lowers costs. Monitor your usage at console.anthropic.com/settings/billing.

Can I customize this for my specific project?

Yes. All Claude Code behavior can be customized through CLAUDE.md (project rules), .claude/settings.json (permissions), and .claude/skills/ (domain knowledge). The most impactful customization is adding your project’s specific patterns, conventions, and common commands to CLAUDE.md so Claude Code follows your standards from the start.

What happens when Claude Code makes a mistake?

Claude Code creates files and edits through standard filesystem operations, so all changes are visible in git diff. If a change is wrong, revert it with git checkout -- <file> for a single file or git stash for all changes. Claude Code does not make irreversible changes unless you explicitly allow destructive commands in settings.json.

Practical Details

When working with Claude Code on this topic, keep these implementation details in mind:

Project Configuration. Your CLAUDE.md should include specific references to how your project handles this area. Include file paths, naming conventions, and any project-specific patterns that differ from defaults. Claude Code reads this file at session start and uses it to guide all operations.

Integration with Existing Tools. Claude Code works alongside your existing development tools rather than replacing them. It respects .gitignore for file visibility, uses your project’s installed dependencies, and follows the build/test scripts defined in package.json (or equivalent). Ensure your toolchain is working correctly before involving Claude Code.

Performance Considerations. For large codebases (10,000+ files), Claude Code’s file scanning can be slow if not properly scoped. Use .claudeignore to exclude generated directories (dist, build, .next, coverage) and dependency directories (node_modules, vendor). This typically reduces scan time by 80-90%.

Version Control Integration. All changes Claude Code makes are regular filesystem operations visible to git. Use git diff after each significant change to review what was modified. For experimental changes, create a branch first with git checkout -b experiment/topic so you can easily discard or keep the results.

Build yours → Create a custom CLAUDE.md with our Generator Tool.

Estimate tokens → Calculate your usage with our Token Estimator.

Try it: Estimate your monthly spend with our Cost Calculator.