skills/blender-scripting/SKILL.md
Write and run Blender Python scripts for 3D automation and procedural modeling. Use when the user wants to automate Blender tasks, create 3D models from code, run headless scripts, manipulate scenes, batch process .blend files, build geometry with bmesh, apply modifiers, generate procedural shapes, or import/export 3D models using the bpy API.
npx skillsauth add tusosos/manus-knowledge-base blender-scriptingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Automate Blender tasks and create 3D models procedurally using Python and the bpy API. Run scripts headlessly from the terminal to manipulate scenes, build geometry with bmesh, apply modifiers, batch process files, and import/export models.
blender --background --python script.py
blender myfile.blend --background --python script.py
blender --background --python script.py -- --output /tmp/result.png --scale 2.0
import bpy
def clear_scene():
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for block in bpy.data.meshes:
if block.users == 0:
bpy.data.meshes.remove(block)
import bpy, math
from mathutils import Vector
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
cube = bpy.context.active_object
cube.location = (3, 0, 1)
cube.rotation_euler = (0, 0, math.radians(45))
cube.scale = (1, 2, 0.5)
vertices = [(-1,-1,0), (1,-1,0), (1,1,0), (-1,1,0),
(-1,-1,2), (1,-1,2), (1,1,2), (-1,1,2)]
faces = [(0,1,2,3), (4,5,6,7), (0,1,5,4), (2,3,7,6), (0,3,7,4), (1,2,6,5)]
mesh = bpy.data.meshes.new("CustomBox")
mesh.from_pydata(vertices, [], faces)
mesh.update()
obj = bpy.data.objects.new("CustomBox", mesh)
bpy.context.collection.objects.link(obj)
import bmesh
bm = bmesh.new()
v1 = bm.verts.new((0, 0, 0))
v2 = bm.verts.new((1, 0, 0))
v3 = bm.verts.new((1, 1, 0))
v4 = bm.verts.new((0, 1, 0))
bm.faces.new((v1, v2, v3, v4))
bmesh.ops.extrude_face_region(bm, geom=bm.faces[:])
bmesh.ops.subdivide_edges(bm, edges=bm.edges[:], cuts=2)
mesh = bpy.data.meshes.new("Result")
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new("Result", mesh)
bpy.context.collection.objects.link(obj)
obj = bpy.context.active_object
sub = obj.modifiers.new("Subdivision", 'SUBSURF')
sub.levels = 2
mirror = obj.modifiers.new("Mirror", 'MIRROR')
mirror.use_axis = (True, False, False)
array = obj.modifiers.new("Array", 'ARRAY')
array.count = 5
array.relative_offset_displace = (1.1, 0, 0)
solid = obj.modifiers.new("Solidify", 'SOLIDIFY')
solid.thickness = 0.1
bevel = obj.modifiers.new("Bevel", 'BEVEL')
bevel.width = 0.05
bevel.segments = 3
# Apply permanently
bpy.context.view_layer.objects.active = obj
bpy.ops.object.modifier_apply(modifier="Subdivision")
curve_data = bpy.data.curves.new("MyCurve", type='CURVE')
curve_data.dimensions = '3D'
spline = curve_data.splines.new('BEZIER')
spline.bezier_points.add(3)
for i, (x, y, z) in enumerate([(0,0,0), (1,1,0), (2,0,1), (3,1,1)]):
pt = spline.bezier_points[i]
pt.co = (x, y, z)
pt.handle_type_left = pt.handle_type_right = 'AUTO'
curve_data.bevel_depth = 0.1
obj = bpy.data.objects.new("MyCurve", curve_data)
bpy.context.collection.objects.link(obj)
bpy.ops.wm.obj_import(filepath="/path/model.obj")
bpy.ops.import_scene.fbx(filepath="/path/model.fbx")
bpy.ops.import_scene.gltf(filepath="/path/model.glb")
bpy.ops.wm.obj_export(filepath="/path/output.obj")
bpy.ops.export_scene.gltf(filepath="/path/output.glb", export_format='GLB')
mat_red = bpy.data.materials.new("Red")
mat_red.diffuse_color = (1, 0, 0, 1)
obj.data.materials.append(mat_red)
for i, poly in enumerate(obj.data.polygons):
poly.material_index = 0 if i % 2 == 0 else 1
import glob
for filepath in glob.glob("/path/to/*.blend"):
bpy.ops.wm.open_mainfile(filepath=filepath)
for obj in bpy.data.objects:
if obj.type == 'MESH':
print(f" {obj.name}: {len(obj.data.vertices)} verts")
bpy.ops.wm.save_as_mainfile(filepath=filepath.replace(".blend", "_processed.blend"))
User request: "Generate a spiral staircase with 20 steps"
import bpy, math
def create_spiral_staircase(steps=20, radius=3, height=6):
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
step_height = height / steps
for i in range(steps):
angle = (2 * math.pi * i) / steps
x, y = radius * math.cos(angle), radius * math.sin(angle)
bpy.ops.mesh.primitive_cube_add(size=1, location=(x, y, i * step_height),
scale=(1.5, 0.4, step_height * 0.8))
bpy.context.active_object.rotation_euler.z = angle
bpy.ops.mesh.primitive_cylinder_add(radius=0.2, depth=height, location=(0, 0, height/2))
create_spiral_staircase()
bpy.ops.wm.save_as_mainfile(filepath="/tmp/staircase.blend")
User request: "Export every mesh in my .blend as a separate OBJ"
import bpy, os
output_dir = "/tmp/exports"
os.makedirs(output_dir, exist_ok=True)
for obj in bpy.data.objects:
if obj.type == 'MESH':
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
bpy.ops.wm.obj_export(filepath=os.path.join(output_dir, f"{obj.name}.obj"),
export_selected_objects=True)
Run: blender scene.blend --background --python export_all.py
User request: "Generate a terrain mesh using sine waves"
import bpy, bmesh, math, random
bm = bmesh.new()
res, size = 50, 20
verts = []
for i in range(res):
row = []
for j in range(res):
x, y = (i/res - 0.5) * size, (j/res - 0.5) * size
z = math.sin(x*0.5) * math.cos(y*0.5) * 2 + random.uniform(-0.2, 0.2)
row.append(bm.verts.new((x, y, z)))
verts.append(row)
for i in range(res-1):
for j in range(res-1):
bm.faces.new((verts[i][j], verts[i+1][j], verts[i+1][j+1], verts[i][j+1]))
mesh = bpy.data.meshes.new("Terrain")
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new("Terrain", mesh)
bpy.context.collection.objects.link(obj)
--background when running from the terminal to skip the GUIfrom_pydata() for simple static meshes; use bmesh for advanced operationsmesh.update() after from_pydata() and bm.free() after bmeshbpy.data for direct data access (fast); use bpy.ops for complex operationsbpy.ops callsmathutils for vector math and matrix operations (bundled with Blender Python)tools
Download video and audio from YouTube and other platforms with yt-dlp. Use when a user asks to download YouTube videos, extract audio from videos, download playlists, get subtitles, download specific formats or qualities, batch download, archive channels, extract metadata, embed thumbnails, download from social media platforms (Twitter, Instagram, TikTok), or build media ingestion pipelines. Covers format selection, audio extraction, playlists, subtitles, metadata, and automation.
development
Download YouTube videos with customizable quality and format options. Use this skill when the user asks to download, save, or grab YouTube videos. Supports various quality settings (best, 1080p, 720p, 480p, 360p), multiple formats (mp4, webm, mkv), and audio-only downloads as MP3.
development
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like "the xlsx in my downloads") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.
development
Use when you have a spec or requirements for a multi-step task, before touching code