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
- Blender Python API Documentation
- Blender official site
- Claude
- Anthropic Documentation
- Blender Manual — Scripting & Extending Blender
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
- 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.
