| name | blender |
| description | Drive Blender via HTTP by sending Python code to localhost:5656. Use when the user wants to automate Blender, inspect scenes, take screenshots, or perform general Blender operations. For video editing (VSE) or 3D scene manipulation, the specialized blender-vse and blender-3d skills provide deeper guidance. |
Blender — Main Skill
You can drive Blender by sending Python code via HTTP POST to localhost:5656.
Sending commands
Always use heredoc to avoid shell quoting issues with Python code:
curl -s localhost:5656 --data-binary @- <<'PYEOF'
import bpy
scene = bpy.context.scene
result_expression
PYEOF
For simple one-expression queries, heredoc still works fine:
curl -s localhost:5656 --data-binary @- <<'PYEOF'
bpy.data.objects.keys()
PYEOF
Important: Always use <<'PYEOF' (quoted delimiter) so the shell doesn't
expand $variables or backticks inside the Python code.
Large scripts
For scripts longer than ~100 lines, write to a temp file and exec it instead of
inlining in the heredoc. This avoids shell/curl issues with large POST bodies:
cat > /tmp/blender_script.py <<'PYEOF'
import bpy
PYEOF
curl -s localhost:5656 --data-binary @- <<'PYEOF'
exec(open("/tmp/blender_script.py").read())
PYEOF
Response format
{"ok": true, "result": "<last expression value>", "output": "<stdout>"}
{"ok": false, "error": "<error message>", "output": "<stdout before error>"}
Before starting work
Start Blender (or connect to an already-running instance) with:
python3 start_server.py
python3 start_server.py /path/to/scene.blend
This blocks until the server is ready and returns the Blender version. It never launches
a second instance — if Blender is already running, it reuses it.
After starting, inspect the scene before making changes — never assume a clean scene.
The user may have work in progress. Run bpy.data.objects.keys() first.
Output directory
An OUTPUT variable is automatically available in every code execution. It points to
the output/ directory. Use it for all file output — screenshots, renders, exports:
f"{OUTPUT}/screenshot.png"
f"{OUTPUT}/render.mp4"
Visual feedback loop
CRITICAL: Screenshots are a TWO-STEP process. The server only returns JSON, never image data.
NEVER use curl -o to save a screenshot — the response is JSON, not an image. Saving it as PNG
will produce a corrupt file that crashes Claude Code when read. Always use the pattern below.
Screenshot the Blender UI:
curl -s localhost:5656 --data-binary @- <<'PYEOF'
bpy.ops.screen.screenshot(filepath=f"{OUTPUT}/blender_ui.png")
PYEOF
Render a frame and inspect:
curl -s localhost:5656 --data-binary @- <<'PYEOF'
scene = bpy.context.scene
scene.render.filepath = f"{OUTPUT}/render.png"
scene.render.image_settings.file_format = "PNG"
scene.render.resolution_percentage = 50
bpy.ops.render.render(write_still=True)
PYEOF
Then read the rendered image to see the output.
Use this for iterating: change -> screenshot/render -> inspect -> adjust.
Fast iteration
Rendering is slow. Minimize render calls and use the lowest quality that answers your question:
- Layout/positioning — use screenshots (
bpy.ops.screen.screenshot), no render needed
- Quick composition check —
resolution_percentage = 25 and BLENDER_WORKBENCH engine
- Lighting/materials check —
resolution_percentage = 25 with EEVEE
- Final output — full resolution, intended engine
- Hard-to-validate visuals — ask the user to check the viewport directly. They can
see real-time rendered view, selection highlights, and interactive feedback that
screenshots and renders may miss. This is especially useful for geometry nodes output,
volumetrics, and complex material effects.
- Temporal effects (animation, audio sync, flickering) — ask the user to play back
the timeline and report what they see. Screenshots and single-frame renders cannot
validate timing, sync, or motion. Don't render video just to check — the user's
viewport playback is instant and more informative.
Only increase quality once the scene is right. Don't render after every small change — batch adjustments and render once to verify. When in doubt, ask the user to verify in the viewport rather than burning time on renders.
Common patterns
Inspect scene
bpy.data.objects.keys()
Get object properties
obj = bpy.data.objects["Cube"]
{"location": list(obj.location), "rotation": list(obj.rotation_euler), "scale": list(obj.scale)}
Check output path
curl -s localhost:5656
Returns {"ok": true, "output": "output/"}.
Error recovery
If Blender crashes (connection refused), restart with:
pkill -x Blender 2>/dev/null || true; sleep 1
python3 start_server.py
Check crash dumps at ~/Library/Logs/DiagnosticReports/Blender-*.ips
Logging and debugging
All execution output (print statements AND Python logging to stderr) streams to
output/agent.log in real-time — even while a request is still running. This
is critical for debugging long-running or hung operations.
Watching live output
tail -f output/agent.log
This shows:
EXEC line when a request starts (with code snippet)
- Live
print() and logging output as the code runs
OK/ERR summary when the request completes
Debugging hangs
If a curl request to Blender hasn't returned:
tail the log to see where it's stuck
- If Blender is frozen:
pkill -9 -x Blender to force-kill, then restart
Adding debug output from addon code
Python logging (stderr) is captured alongside print() (stdout). Both stream
to the log file in real-time. Use print() for progress in scripts sent via HTTP,
and logging.getLogger("your_module").info() inside addon code.