jablonkai/skills

blender

remote-control a running Blender by Python via a local bridge — bmesh/modifier modeling, shader and geometry nodes, animation, rigging, physics, Grease Pencil and the VSE, EEVEE/Cycles stills and video, glTF/FBX/USD/OBJ/STL export

First seen Jul 31, 2026

Installation

$ npx skills add jablonkai/skills --skill blender

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from jablonkai/skills · top by installs.

npx skills add jablonkai/skills

Browse all from jablonkai/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 1
License LICENSE
Default branch main
Open issues 4
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 11,180 B
  • docs SUMMARY.md 774 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 41 installs

SKILL.md

Blender Control

Blender (/Applications/Blender.app) is scriptable in Python against bpy, bmesh and mathutils. Drive it through the Blender Bridge — a script running inside a running Blender GUI that executes whatever build script is POSTed to 127.0.0.1:8736. Because it runs in the live session on the main thread, bpy.data writes are legal, the viewport updates as you build, and viewport screenshots work. Everything documented here was executed and verified on Blender 5.2.0 LTS (bundled Python 3.13).

  • [scripts/blender-bridge.py](scripts/blender-bridge.py) — the bridge to run inside Blender.
  • [scripts/blender-send.sh](scripts/blender-send.sh) — send a .py file (or -c 'inline')

and print its captured output; --ping checks it's up, --state dumps the scene as JSON.

  • [scripts/example-product-shot.py](scripts/example-product-shot.py) — model → material →

three-point light → camera → measure → render.

  • [scripts/example-animated-logo.py](scripts/example-animated-logo.py) — text → keyframes →

geometry-nodes scatter → frame sequence → video.

Read [references/gotchas.md](references/gotchas.md) before writing anything. Blender's API changed substantially in 5.x and most of what a model has memorised — action.fcurves, scene.nodetree, mod["Socket2"], BLENDEREEVEENEXT — is now wrong.

The control loop

  1. User starts the bridge (one-time). This cannot be done remotely — if

bash scripts/blender-send.sh --ping gets no answer, ask the user to do one of: - copy [scripts/blender-bridge.py](scripts/blender-bridge.py) to ~/Library/Application Support/Blender/5.2/scripts/startup/blender_bridge.py and restart Blender — it then starts automatically on every launch (recommended), or - open the Scripting workspace, paste the file into the text editor, press Run Script (⌥P) — lasts for that session, or - install it as an add-on via Preferences ▸ Add-ons ▸ Install.

A successful ping returns {"ok": true, "bridge": "blender", "version": "5.2.0 LTS", "file": null, ...}.

  1. Look before you build: bash scripts/blender-send.sh --state returns objects and

types, collections, materials, node groups, frame range, resolution, engine and whether the file has unsaved changes — cheaper than writing a script to ask.

  1. Write a build script to the scratchpad, starting from the cheatsheet below and the

right reference file.

  1. Send it: OUT=/path/to/outdir bash scripts/blender-send.sh /path/build.py. The bridge

runs it in the live session and returns the script's captured stdout; on error it returns the traceback and the sender exits non-zero. BLENDERSENDTIMEOUT=900 (seconds) for heavy renders and bakes.

  1. Feedback: returned stdout first; metrics(...) for structured geometry checks;

snapshot(...) for a viewport PNG to Read; render(...) for the real thing.

  1. Iterate: inspect, fix, re-send. Scripts must be re-runnable — build inside

stage("name") so a re-send replaces its own output instead of stacking duplicates.

Reference routing

Task Read
Anything, before you start [gotchas.md](references/gotchas.md)
Data-blocks, transforms, collections, depsgraph, scenes [api-reference.md](references/api-reference.md)
bmesh, modifiers, curves, text, UVs, booleans [modeling.md](references/modeling.md)
Materials, shader nodes, textures, world/HDRI, lights, cameras [shading.md](references/shading.md)
Procedural geometry, scattering, instancing, fields, zones [geometry-nodes.md](references/geometry-nodes.md)
Keyframes, F-curves, drivers, NLA, armatures, IK, shape keys [animation-rigging.md](references/animation-rigging.md)
Rigid body, cloth, soft body, particles, hair, fluid, bakes [simulation.md](references/simulation.md)
EEVEE/Cycles settings, passes, compositor, output, video [rendering.md](references/rendering.md)
2D / toon linework, Line Art [grease-pencil.md](references/grease-pencil.md)
Editing clips, titles, transitions, final cut [vse.md](references/vse.md)
glTF, FBX, USD, Alembic, OBJ, STL, .blend append/link [io-formats.md](references/io-formats.md)

Injected namespace

Pre-imported: bpy, bmesh, mathutils, Vector, Matrix, Euler, Quaternion, math, os, json, plus OUT (from $OUT, also os.environ["OUT"]) and these helpers:

Helper Does
stage(name) get-or-recreate a named collection, make it active — makes re-sends idempotent
sync() viewlayer.update(); required before reading matrixworld
evaluated(ob) the depsgraph-evaluated object (modifier / geometry-nodes result)
frame(n) frame_set(n) + depsgraph update
metrics(objs, path=) dict + JSON: counts, verts/tris, world bbox, materials, frame range
snapshot(path, view=, shading=, fit=) viewport PNG (fast visual check)
render(path, engine=, samples=, ...) real EEVEE/Cycles still; restores every setting
frame_view(objs, view=) aim the viewport (ISO/FRONT/TOP/CAMERA/…)
world_bounds(objs) world-space (min, max)
fcurves(ob) / fcurve(ob, path, i) / channelbag(ob) slotted-action F-curve access
ui_override(area) context override for the few bpy.ops that need an editor

Cheatsheet

Units are metres and radians. Prefer the data API; bpy.ops is for mode changes, smart_project, rigidbody.*, nla.bake, and importers/exporters.

Build geometry (re-runnable)

coll = stage("hero_v1")                       # owns its own output; safe to re-send
me = bpy.data.meshes.new("Body")
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=2.0)
bmesh.ops.bevel(bm, geom=list(bm.edges), offset=0.06, segments=3, affect="EDGES")
bm.to_mesh(me); bm.free()
ob = bpy.data.objects.new("Body", me)
coll.objects.link(ob)
ob.location = (0, 0, 1)
sync()                                        # before ANY matrix_world read

Material

m = bpy.data.materials.new("Shell")
m.use_nodes = True
b = m.node_tree.nodes["Principled BSDF"]
b.inputs["Base Color"].default_value = (0.75, 0.2, 0.15, 1.0)
b.inputs["Roughness"].default_value = 0.35
b.inputs["Metallic"].default_value = 0.0      # no "Specular"/"Emission" inputs since 4.0
ob.data.materials.append(m)

Camera aimed at the subject

cd = bpy.data.cameras.new("Cam"); cd.lens = 50
cam = bpy.data.objects.new("Cam", cd); coll.objects.link(cam)
cam.location = (6, -6, 4)
cam.rotation_euler = (Vector((0, 0, 1)) - cam.location).to_track_quat("-Z", "Y").to_euler()
bpy.context.scene.camera = cam

Feedback

m = metrics([ob], path=OUT + "/metrics.json")
print("METRICS", json.dumps(m))               # comes straight back through the sender
print(snapshot(OUT + "/check.png", view="ISO"))
print(render(OUT + "/hero.png", engine="BLENDER_EEVEE", samples=64,
             width=1280, height=720))

Keyframes

for f, z in ((1, 0.0), (24, 3.0), (48, 0.0)):
    ob.location.z = z
    ob.keyframe_insert("location", index=2, frame=f)
for kp in fcurves(ob)[0].keyframe_points:     # NOT action.fcurves — that is gone
    kp.interpolation = "BEZIER"
    kp.easing = "EASE_IN_OUT"

Export

bpy.context.view_layer.objects.active = ob; ob.select_set(True)
bpy.ops.export_scene.gltf(filepath=OUT + "/hero.glb", export_format="GLB",
                          use_selection=True)
bpy.ops.wm.obj_export(filepath=OUT + "/hero.obj", export_selected_objects=True)
bpy.ops.wm.save_as_mainfile(filepath=OUT + "/hero.blend", copy=True)

Verification

  • Returned stdout is the immediate signal — print(...) comes straight back.
  • metrics(...) is the structured check (object/vert/tri counts, world bbox, materials).

Write it to $OUT/metrics.json and Read it to confirm geometry without eyeballing.

  • snapshot(...) is the fast visual check — a viewport OpenGL PNG, no full render.

shading="RENDERED" previews materials and lights.

  • render(...) for the deliverable. It raises a diagnostic RuntimeError if Blender

reported success but wrote nothing.

  • Hand-off: a .glb/.blend in $OUT opens in whatever the user already has.

Security

Installing this skill means running a code-execution server on the user's machine. Say so before asking them to start the bridge.

  • The bridge binds 127.0.0.1:8736 (BLENDERBRIDGEPORT) and executes any Python POSTed

to /run inside the live session — the user's privileges, the user's open file. Requests carry no authentication: every local process, and every other user on a shared machine, can drive Blender through it.

  • Web pages cannot. Requests carrying an Origin header or a cross-site

Sec-Fetch-Site are rejected with 403, so a page in the user's browser can't reach the bridge. That check is the only gate — there is no token.

  • The install choice decides how long the port stays open. Scripting workspace ▸ Run

Script lasts until Blender quits. The scripts/startup/ install is recommended above for convenience, but it makes every Blender launch listen, whether or not an agent is driving it — offer the trade-off rather than assuming it.

  • To stop the bridge, quit Blender. There is no remote shutdown; the port is released

with the process.

Safety

  • The open file may hold unsaved work. Never call bpy.ops.wm.read_homefile() or

bpy.data.batch_remove without asking. stage() is the non-destructive default; it isolates geometry but not scene-level settings.

  • Renders, bakes and sims block the main thread — the UI freezes until they finish.

Iterate small, and warn the user before anything long.

  • Save into $OUT, not over the user's .blend; saveasmainfile(..., copy=True) leaves

the live session pointed at their own file.