Skip to content

Blender × Claude — Python Scripting Primer

Updated: 2026-05*

1. Introduction

This material explains how to use the AI assistant Claude alongside the 3D software Blender to write Python scripts that automatically generate, edit, and animate 3D scenes. Repetitive operations that take time with mouse interaction, regular placements, and parametric shaping can all be handled by having Claude interactively generate Python code.

1.1 References

1.2 Goals

There are three goals for this material.

First, to be able to use Blender’s Scripting workspace to run basic Python scripts that use the bpy module. Second, to learn how to convey “what you want to make” to Claude at an appropriate level of granularity so that it generates runnable scripts. Third, to acquire the workflow of fixing errors and mismatches between intent and output by collaborating with Claude.

1.3 Intended Audience and Prerequisites

This material targets students in media art and design, and assumes basic Blender operation experience (adding objects, switching between Object Mode and Edit Mode, rendering). No prior Python knowledge is required. Claude is fully usable from the browser version (claude.ai), and this material requires no special development environment.

2. Setup

We run Blender and Claude independently and bridge them via human copy-and-paste. Direct Blender–Claude integrations via MCP (Model Context Protocol) and similar mechanisms exist, but this material covers manual operation only.

2.1 Blender Version

Blender 5.1 or later is recommended. The bpy API changes between versions, so always tell Claude which version you are using. The version can be checked from the menu at the top right of the screen.

2.2 The Scripting Workspace

Select the Scripting tab from the top of the Blender window. By default, the screen is divided as follows.

  • Large central area: Text Editor
    • For writing, saving, and running multi-line scripts
  • Bottom-left: Python Console
    • For executing Python one line at a time, interactively
  • Bottom-right: Info (execution log)
    • Logs all mouse-driven actions as their corresponding Python commands

The top of the screen holds the 3D Viewport and the Outliner / Properties.

2.3 Testing the Python Console

Place the cursor in the Python Console and type the following code, pressing Enter after each line.

import bpy
bpy.ops.mesh.primitive_cube_add()

If a new Cube appears in the 3D Viewport, object manipulation from Python is working.

2.4 Preparing Claude

Open claude.ai in a browser and create a new chat. If you plan to generate Blender scripts continuously, set up a Project or Style so that you don’t have to re-enter your Blender version and working context each time.

Tip: when using Projects, putting an instruction like “Write bpy scripts for Blender 5.x. Return code blocks only and keep explanation minimal.” in the Project Instructions will keep responses code-focused.

3. The Basics of bpy

bpy is the top-level module of the Blender Python API and the entry point to every element inside Blender. This chapter organizes the minimum bpy structure you should know to request scripts from Claude effectively.

3.1 Main Submodules

  • bpy.ops
    • Role: invoke “Operators” — the actions available from Blender’s UI — from code
    • Example: bpy.ops.mesh.primitive_cube_add()
  • bpy.data
    • Role: access all data in the scene (Mesh, Material, Object, etc.)
    • Example: bpy.data.objects["Cube"]
  • bpy.context
    • Role: handle “context” information such as the currently selected object and active scene
    • Example: bpy.context.active_object
  • bpy.types
    • Role: access Blender’s internal type definitions (used for type hints and inspecting properties)

3.2 Extracting Code from the Info Area

When you perform a mouse operation in Blender, the corresponding Python command is automatically logged in the Info area. For example, running Add Mesh → Cube via the menu produces output like this in the Info area.

bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0))

Selecting and copying this output gives you the Python representation of your GUI operation instantly. It also makes useful reference material when asking Claude.

3.3 Running Scripts from the Text Editor

Multi-line scripts go in the Text Editor. Press New at the top of the Text Editor to create a new text, paste the code, and click Run Script (the ▶ icon). The keyboard shortcut is Alt + P.

4. Having Claude Write Blender Scripts

Clearly conveying “what, on which version, and against which scene state” determines the quality of Claude’s output. This chapter organizes the components of an effective prompt.

4.1 Basic Prompt Structure

At a minimum, the following four elements should be included.

First, the Blender version (e.g., Blender 5.1). Second, the current scene state (empty scene, or specific objects already present). Third, a concrete description of the desired result. Fourth, your preferred output format (e.g., something that can be pasted into the Text Editor and run with Run Script).

Example prompt:

I am using Blender 5.1.
The scene is empty (the default Cube, Camera, and Light have been deleted).
Write a script that places 12 Spheres at equal intervals on a circle of radius 10.
Return the whole script as one code block, in a form that can be run from the Text Editor's Run Script.

4.2 Running the Script

Apply the code returned by Claude to Blender as follows.

First, copy the code block from Claude’s response. Second, press New in Blender’s Text Editor and paste the code. Third, run it with Run Script (or Alt + P). Fourth, verify the result in the 3D Viewport.

4.3 Handling Errors

When a script execution fails, an error message appears in red at the bottom status bar of Blender. Open the system console via Window → Toggle System Console (on Windows), or, if you launched Blender from a terminal (on macOS / Linux), you can see the full error traceback there.

Paste the full error message into Claude with “I got the following error, please fix it” and in most cases you will get back a corrected version.

Tip: paste the entire error message including line numbers and the full traceback, not just the last line. Information is often missing if you only copy the final line.

4.4 Resetting the Scene

Since the scene tends to get cluttered during experimentation, it is useful to put a reset block at the top of your script. The following pattern is unlikely to trigger context errors even when run from the Text Editor, and is safe for beginners.

import bpy

# Delete all existing objects
for obj in list(bpy.data.objects):
    bpy.data.objects.remove(obj, do_unlink=True)

# Also remove orphaned mesh data
for block in list(bpy.data.meshes):
    if block.users == 0:
        bpy.data.meshes.remove(block)

Note: the combination bpy.ops.object.select_all(action='SELECT') + bpy.ops.object.delete() can fail with RuntimeError: context is incorrect if the mouse focus is not on the 3D Viewport. The bpy.data-based pattern above does not depend on context, so it runs reliably from the Text Editor.

5. Practice 1 — Arranging Objects in a Regular Pattern

The most fundamental case: arranging multiple objects in a regular layout.

5.1 Arranging in a Grid

Start by giving Claude the following instruction.

Example prompt:

Assume Blender 5.1 with an empty scene.
Write a script that places Cubes in a 10x10 grid.
The spacing between Cubes is 2.0.

The returned script will roughly take this structure.

import bpy

spacing = 2.0
grid_size = 10

for i in range(grid_size):
    for j in range(grid_size):
        x = i * spacing
        y = j * spacing
        bpy.ops.mesh.primitive_cube_add(size=1.0, location=(x, y, 0))

5.2 Randomizing Height

Then ask Claude for an addition:

Example follow-up prompt:

Randomize the Z height of each Cube in the range 0 to 3.
Fix the seed to 42.

You’ll get back a script rewritten to use the random module. Note that random.seed() is set for reproducibility.

5.3 Bonus: Waving with a Sine Curve

If you decide Z height with math.sin instead of random values, you get wave-like terrain. Asking Claude to “modify so Z height is determined by sin(x * 0.5) + cos(y * 0.5)” will rewrite the script accordingly.

6. Practice 2 — Assigning Materials

An example of applying color and shader settings to objects in bulk from Python.

6.1 Principled BSDF Basics

When setting Blender’s standard shader, Principled BSDF, from Python, the node structure can be organized as follows.

  • Material (the material itself)
    • use_nodes: True
    • node_tree: holds the Shader Nodes internally
      • Principled BSDF
        • Base Color: (R, G, B, 1.0)
        • Roughness: 0.0–1.0
        • Metallic: 0.0–1.0
      • Material Output
        • Surface input ← BSDF socket of Principled BSDF

6.2 Assign a Random Color to Every Object

Example prompt:

For every Mesh Object in the scene, write a script that assigns a material with a random color.
Use Principled BSDF and randomize only the Base Color.
Fix the seed to 42.

The skeleton of the generated script looks like this.

import bpy
import random

random.seed(42)

for obj in bpy.context.scene.objects:
    if obj.type != 'MESH':
        continue

    mat = bpy.data.materials.new(name=f"Mat_{obj.name}")
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes.get("Principled BSDF")

    r = random.random()
    g = random.random()
    b = random.random()
    bsdf.inputs["Base Color"].default_value = (r, g, b, 1.0)

    if obj.data.materials:
        obj.data.materials[0] = mat
    else:
        obj.data.materials.append(mat)

Note: the value of bsdf.inputs["Base Color"] must be a length-4 tuple (RGBA). Passing a length-3 tuple causes an error.

6.3 Bonus: Turn On Emission to Make Objects Glow

Asking Claude to “set Emission Color to the same value as Base Color and set Emission Strength to 2.0” produces self-illuminating materials. Combined with the Cycles renderer, you can build a no-lights setup.

7. Practice 3 — Adding Animation

An example of inserting keyframes from Python on an object’s location, rotation, and scale.

7.1 Up-and-Down Motion of a Single Object

Example prompt:

In Blender 5.1, for the currently selected object,
write a script that inserts keyframes from frame 1 to 120
moving it up and down along Z by sin(frame * 0.1) * 2.

The skeleton of the generated script is as follows.

import bpy
import math

obj = bpy.context.active_object

for frame in range(1, 121):
    bpy.context.scene.frame_set(frame)
    obj.location.z = math.sin(frame * 0.1) * 2
    obj.keyframe_insert(data_path="location", index=2, frame=frame)

index=2 means the third element of location (Z). To keyframe X, Y, or Z individually, specify 0, 1, or 2.

7.2 Adding a Phase Difference Across All Cubes to Create a Wave

Adding a position-dependent phase difference to the grid layout from Practice 1 produces a wave-like animation.

Example prompt:

For every Cube in the scene, insert keyframes from frame 1 to 240
that vary Z height by sin(frame * 0.1 + (x + y) * 0.3).
Use each Cube's world X and Y coordinates for x and y.

7.3 Bonus: Changing Interpolation to LINEAR

Keyframe interpolation defaults to BEZIER (smooth). For mechanical motion, change to LINEAR or CONSTANT. Asking Claude to “change the interpolation mode of all fcurves to LINEAR” will add the corresponding script.

8. Practice 4 — Controlling Geometry Nodes from Python

Blender’s Geometry Nodes is a powerful procedural modeling system. The nodes themselves are built in a node-based interface, but adding it as a Modifier and setting input socket values is possible from Python.

8.1 Adding the Modifier

Example prompt:

Write a script that adds a Geometry Nodes Modifier to the selected object
and assigns a new NodeTree.
Assume Blender 5.1.

The skeleton of the generated script:

import bpy

obj = bpy.context.active_object
mod = obj.modifiers.new(name="GeoNodes", type='NODES')

# Create a new NodeTree
node_tree = bpy.data.node_groups.new(name="MyGeoNodes", type='GeometryNodeTree')
mod.node_group = node_tree

# Define input/output sockets (new API since 4.0, still used in 5.x)
node_tree.interface.new_socket(name="Geometry", in_out='INPUT', socket_type='NodeSocketGeometry')
node_tree.interface.new_socket(name="Geometry", in_out='OUTPUT', socket_type='NodeSocketGeometry')

# Add Input / Output nodes
input_node = node_tree.nodes.new(type='NodeGroupInput')
output_node = node_tree.nodes.new(type='NodeGroupOutput')
input_node.location = (-200, 0)
output_node.location = (200, 0)

# Connect input and output
node_tree.links.new(input_node.outputs[0], output_node.inputs[0])

Note: node_tree.interface.new_socket() was introduced in Blender 4.0 and continues to work in 5.x. The 3.x series used node_tree.inputs.new(), so older articles found on the web may not run as-is. Telling Claude explicitly to “use the new interface API for Blender 5.x” makes things reliable.

8.2 Adding and Connecting Nodes

You can also have Claude describe a workflow that inserts Distribute Points on Faces and Instance on Points into an existing Geometry Nodes Modifier to instance large numbers of objects on a mesh surface. The more accurately you convey node names and input/output socket names, the more accurate the output script.

9. Patterns for Collaborating with Claude

This section organizes practical patterns for using Claude in Blender scripting as an interactive collaborator rather than a one-shot code generator.

9.1 Request Incrementally

Rather than handing over a complex requirement all at once, splitting it into “minimum implementation → extension → refinement” lets you verify the code at each stage and produces higher final quality.

For example, “place 100 Cubes randomly and add a wave-like animation on Z” can be split into (1) randomly placing Cubes → (2) adding materials → (3) giving every Cube a simple up-and-down on Z → (4) adding position-dependent phase to turn it into a wave. Verifying each stage in Blender before sending the next request makes it easier to isolate where intent and output diverge.

9.2 Paste Error Logs to Request Fixes

When an error occurs, paste both the full traceback and the script you just ran. Stating the before-and-after relationship as “I ran the following script and got the following error” lets Claude pinpoint the cause and return a fixed version.

9.3 Request Modifications to an Existing Script

Pasting a previously written script into Claude and asking “Add the feature ~ to this script” is also effective. Compared to writing from scratch, you get a local change that preserves the existing structure, making debugging easier.

9.4 Use as a Reference

Claude is also useful for investigative questions such as “What is the difference between bpy.context.active_object and bpy.context.object?” or “What is the correct API for adding Geometry Nodes sockets in Blender 5.x?” Note, however, that the Blender API is frequently updated and answers may be based on outdated information, so it is best to ultimately verify against the official documentation.

10. Closing

This material covered combining Blender and Claude to do automatic generation, editing, animation, and procedural modeling of 3D scenes via Python scripts. The important point is to treat Claude not as a “convenient code-writer” but as a “collaborator with knowledge of the Blender API,” and to build the habit of conversing with it while putting your intent into words.

You do not need to memorize all the functions of bpy. If you have a rough sense of what is possible, the concrete syntax and arguments can be left to Claude. Then, by reading the generated code, reporting errors, and requesting modifications in a continuous loop, your understanding of Blender itself deepens naturally.