smoke-gated on 5.2 LTS + 4.5 LTS · exit 0

Blender Python
that actually runs.

Skills, rules, snippets, and starter templates that teach Cursor and Claude Code the bpy that works — backed by runnable examples asserted headless on Blender 5.2 LTS and 4.5 LTS before they ship.

$ git clone https://github.com/TMHSDigital/Blender-Developer-Tools
Render Result 8 of 52 frames

Every render here is code that runs.

These aren't mockups. Each example runs headless on Blender 5.2 LTS and 4.5 LTS in the smoke workflow, asserts its own correctness, and exits non-zero if the API drifted. Each still was rendered by that same script.

Browse all 52 rendered examples and 72 showcase pieces in the gallery →

A smooth candy-red hatchback in front three-quarter view with tinted glass, a chrome sill, five-spoke alloy wheels, and matched headlamps and mirrors either side of one centered grille.
car-mirror-symmetry A stylized hatchback lofted as one half (52 stations, 13-point rings) and completed by the Mirror modifier, evaluated through the depsgraph. Wheels, lamps, grille, door mirrors and handles mirror about object origins parked on the symmetry plane; the grille is authored as a half and welded on it.
A white soccer ball with black pentagon panels and dark stitched seams, pressed into mown pitch turf on a chalked penalty spot with a touchline behind it.
soccer-ball-goldberg A soccer ball as a Goldberg polyhedron: a bmesh icosphere truncated at 1/3 per edge, faces ordered by link-topology walks, panels bound by face vertex count.
Twelve red-and-brass spotlight heads on low and high stands in an open ring, every lens swung to face a glowing amber orb on a brass pedestal.
damped-track-aim Aim constraints via the data API — Object.constraints.new('DAMPED_TRACK') with target and TRACK_Z, not bpy.ops.object.constraint_add in a headless loop. Gallery still: twelve spotlight heads on stands, all swung onto one glowing orb.
Two identical porcelain chess kings on dark plinths: the left, labelled LINKED, glows under an amber key; the right, labelled UNLINKED, stays cool grey.
light-link-studio One key, one hero: a light linked to a receiver collection lights only the hero, proven by two pixel renders in one pass. Linked: 3.6x luminance ratio; unlinked in the same check: the decoy rises 233% while the hero holds at 0.3% drift.
A red and yellow fire hydrant wrapped in a cyan wireframe convex collision hull.
collision-hull-proxy A fire hydrant street prop inside its compound collision shell: four convex pieces hulled by bmesh.ops.convex_hull from a coarse inflated cage. The dense render mesh is never hulled - its hull would measure 380 faces, over the 255-face per-piece engine budget. Closed-form plane tests prove containment, convexity, watertightness, outward winding, and Euler characteristic 2 per piece.
Three ribbed bellows hoses with brass collars on steel foot flanges, banded teal to amber to coral, standing straight, half curled and fully curled by an armature.
armature-bend Rigging end to end in the data API — edit_bones chain construction, name-bound vertex groups with smoothstep blend zones, posing, and depsgraph evaluation — bending a ribbed bellows hose through rest, half, and full curl.
A brass fourteen-tooth gear meshing with a blued pinion and a spoked gunmetal wheel, each bolted through a hub to a dark steel backplate on a walnut plinth.
bmesh-gear A 14-tooth gear built entirely with bmesh — profile ring, face, extrude — with bm.free() in a try/finally, exactly as the ownership contract demands.
A sci-fi corridor built from snapped modular segments, with painted teal wall panels, diamond-plate walkways, hazard-striped trim rails and a lit doorway at the far end.
modular-kit-snap A tiling corridor kit whose open-end boundary verts snap to the tile grid, so instances at 4 m multiples join with zero gap or overlap.
Open the gallery — code, README, and full-size renders →
Showcase 6 of 72 pieces

Budget-conformance props, not API contracts.

Showcase pieces compose shipped skills into a recognizable asset and assert declared budgets — triangle counts, materials, UVs, LODs, colliders, exports. They are not examples. A still that merely rendered is not an assertion.

See all 72 showcase pieces in the gallery →

What's new v0.125.1
Skills 16 loaded

Workflows the AI loads by name.

Each skill is the canonical pattern for one job — operators, panels, bmesh, geometry nodes, slotted actions — plus the mistakes AI assistants actually make there. Where 4.5 LTS and 5.x diverge, both code paths are shown.

addon-scaffolding Scaffold a Blender add-on against the Extensions Platform format with blender_manifest.toml, modular file layout, and the register_classes_factory pattern. Targets Blender 5.2 LTS with 4.5 LTS fallback.
When to use (4)
  • Wants to start a new Blender add-on
  • Mentions bl_info, blender_manifest.toml, "extension", "register_classes_factory"
  • Is migrating an existing add-on from the legacy bl_info format to the Extensions Platform
  • Is unsure which file layout or registration pattern to use for Blender 4.5+ or 5.x
ai-mesh-cleanup Ordered cleanup for an imported generated mesh. Unit scale, transform apply, origin, normals, evaluated triangle count, decimate to budget, convex collider. Targets 5.2 LTS with 4.5 LTS fallback.
When to use (4)
  • Has a generated or scanned GLB/glTF/FBX that is not engine-ready
  • Mentions unit scale, unapplied transforms, triangle budget, LOD, or a collision hull
  • Wants a headless cleanup pass (import in, cleaned mesh out)
  • Is about to run mesh operators on an import without checking scale
bake-high-to-low Cage-bake high-poly surface detail onto a low-poly target as a tangent-space normal map. Cycles CPU, selected-to-active, active image node, UV layer, save_render. Targets 5.2 LTS with 4.5 LTS fallback.
When to use (4)
  • Wants a tangent-space normal map from a dense source onto a game-resolution mesh
  • Mentions cage bake, use_selected_to_active, cage_extrusion, or bpy.ops.object.bake
  • Has an LOD from DECIMATE COLLAPSE (or a retopo) and needs the missing surface detail in a map
  • Is about to call bake from EEVEE, on GPU in CI, or with bake_type=
bl-info-migration Migrate a legacy bl_info-format add-on to the Extensions Platform. Three concrete steps, before-and-after diff, dual-format pattern for backward compatibility, and answers to "is bl_info still supported?" Targets Blender 5.2 LTS.
When to use (5)
  • Has an existing add-on with a bl_info = {...} dictionary at the top of __init__.py
  • Wants to publish to extensions.blender.org or distribute as a .zip for the Extensions Platform
  • Asks "is bl_info dead?" or "do I have to migrate?"
  • Sees a deprecation warning at install time about legacy add-ons
  • Mentions blender_manifest.toml, "Install legacy Add-on", or "the new extension system"
custom-properties Define and bind Blender custom properties via bpy.props using the type annotation form, with PropertyGroup for grouping, PointerProperty for binding, and the four storage location options for Scene/Object/WindowManager/AddonPreferences. Targets 5.2 LTS.
When to use (4)
  • Wants to attach data to objects, scenes, or other Blender datablocks that survives save and load
  • Mentions bpy.props, PropertyGroup, PointerProperty, "custom properties"
  • Is unsure where to store add-on settings
  • Sees a deprecation warning about property assignment
depsgraph-and-evaluated-data Read the actual evaluated geometry the user sees by going through the dependency graph rather than reading raw obj.data. Covers evaluated_get, to_mesh, to_mesh_clear, and the lifetime rules that prevent crashes and memory leaks. Targets Blender 5.2 LTS.
When to use (5)
  • Is writing an exporter, measurement script, or inspection tool
  • Reports that "modifiers are missing from my export" or "the vertex positions don't match what I see"
  • Mentions evaluated_depsgraph_get, evaluated_get, to_mesh, to_mesh_clear
  • Reads from obj.data.vertices and gets unexpectedly raw geometry
  • Needs final positions after armature deformation, shape keys, modifiers, or geometry nodes
drivers-and-app-handlers Drive properties from expressions or other properties via the Driver API, and react to scene events via the bpy.app.handlers callbacks. Covers driver_namespace for Python functions, the new exit_pre handler in 5.1, and the must-be-fast contract for any handler.
When to use (5)
  • Wants property A to follow property B with some math (driver)
  • Asks about driver_add, FCurve.driver, driver.expression, driver_namespace
  • Wants to run code on file save, file load, frame change, depsgraph update, or process exit
  • Mentions bpy.app.handlers, save_pre, load_post, depsgraph_update_post, exit_pre
  • Has a driver expression that fails security checks because it tries to call a Python function
engine-export-presets Unity, Godot, and Unreal glTF/FBX export presets. glTF uses export_yup; FBX uses axis_forward/axis_up plus centimeter scale. Targets 5.2 LTS with 4.5 LTS fallback.
When to use (4)
  • Needs a Unity, Godot, or Unreal export from Blender Python
  • Mentions Y-up, Z-up, centimeter scale, export_yup, axis_forward, or axis_up
  • Is about to pass FBX axis kwargs to bpy.ops.export_scene.gltf
  • Wants a headless preset the later ai-asset-pipeline-template can call
geometry-nodes-python Programmatically construct Geometry Nodes trees in Blender 5.x via bpy.data.node_groups, interface socket creation, node instantiation by RNA name, link wiring, and applying as a NODES modifier. Includes Bundles for grouped sockets.
When to use (4)
  • Wants to build a Geometry Nodes tree from a script rather than the editor
  • Mentions GeometryNodeTree, node_groups.new, tree.interface, tree.links.new
  • Needs to apply a generated GN tree as a modifier on an object
  • Asks about Bundles, Repeat Zones, or for-each Element zones from Python
headless-batch-scripting Run Blender headless via blender --background --python script.py for batch jobs. What changes without a UI, how to avoid UI-dependent operators, the temp_override pattern when ops must be used, and argparse after the -- separator.
When to use (4)
  • Wants to render, export, or process .blend files from a CLI or CI pipeline
  • Mentions blender --background, --python, "headless", "batch render", "no UI"
  • Has an operator script that fails with RuntimeError: Operator bpy.ops.X.Y.poll() failed, context is incorrect
  • Needs to pass arguments to a Blender Python script
mesh-editing-and-bmesh Performant mesh manipulation in Blender. When to use bpy.data vs bpy.ops vs bmesh, the canonical bm.new/free pattern, foreach_set bulk vertex injection, and depsgraph evaluation for modifier-applied geometry. Targets 5.2 LTS.
When to use (5)
  • Wants to create, modify, or read mesh geometry from Python
  • Mentions bmesh, mesh.vertices, foreach_set, from_pydata, evaluated_get
  • Has a script that's slow and uses bpy.ops.mesh.* in a loop
  • Needs the mesh after modifiers (subdivision surface, mirror, geometry nodes) have been applied
  • Asks why their bmesh script crashes on the second run
operators Author Blender operators with bpy.types.Operator, bl_idname conventions, the poll/invoke/execute/modal lifecycle, REGISTER and UNDO options, and defensive context handling. Targets 5.2 LTS with 4.5 LTS compatibility.
When to use (4)
  • Wants to create a new bpy.types.Operator
  • Mentions bl_idname, bl_label, bl_options, "redo panel", "F6 panel", "operator props"
  • Needs an action triggered from a button, menu, keymap, or chat command
  • Asks why their operator's properties don't show up in the redo panel
procedural-materials-and-shaders Build materials and shader graphs from Python by enabling nodes, instantiating shader nodes, setting socket default values, and wiring links. Targets Blender 5.2 LTS EEVEE Next and Cycles. Avoids the (deferred to 2027) Layered Textures roadmap.
When to use (5)
  • Wants to create or edit materials from Python rather than the shader editor
  • Mentions bpy.data.materials, node_tree, ShaderNodeBsdfPrincipled, inputs[...].default_value
  • Needs to generate many materials parametrically (asset libraries, batch import, color variations)
  • Asks about EEVEE Next vs Cycles material differences
  • Asks about "Layered Textures" or "the new shader system" (see version-correctness section)
slotted-actions-animation Animate from Python under the Slotted Actions architecture (data model shipped in Blender 4.4). Action contains Layers contain Strips contain Channelbags. Cross-version channelbag access - action_ensure_channelbag_for_slot is new in 5.0; on 4.4/4.5 LTS use strip.channelbag(slot, ensure=True) or the still-present legacy action.fcurves.
When to use (4)
  • Wants to create or edit animation from Python in Blender 5.x
  • Mentions Action, slot, channelbag, keyframe_insert, fcurves
  • Has older 4.x animation code (action.fcurves.new(...)) that no longer works in 5.x
  • Asks how to insert keyframes programmatically and have them persist
ui-panels Author Blender UI panels with bpy.types.Panel, declarative draw(), bl_space_type and bl_region_type, layout primitives like row/column/split, and conditional UI via .enabled. Targets 5.2 LTS.
When to use (4)
  • Wants a sidebar tab in the 3D viewport, properties editor, or any other space
  • Mentions bpy.types.Panel, bl_space_type, bl_region_type, "N panel", "sidebar"
  • Needs to expose properties or operators in a custom UI
  • Asks why a property is showing without a label or in the wrong column
vse-python Build Video Sequence Editor timelines from Python. SequenceEditor.strips vs .sequences, new_effect length vs frame_end, and the 5.2 COLOR strip width/height bake from scene resolution.
When to use (5)
  • Builds or inspects a VSE / sequencer timeline from a script
  • Mentions sequence_editor, strips, sequences, new_effect, COLOR strips, or StripTransform
  • Hits AttributeError: 'SequenceEditor' object has no attribute 'sequences' on 5.x
  • Hits TypeError on new_effect kwargs (frame_end vs length)
  • Renders a sequencer composite whose COLOR cells ignore transform.scale_* after a resolution change
Rules 9 active

Anti-patterns, caught before they ship.

Guardrails that load whenever a matching file is open, for the failure modes that make Blender Python look right and run wrong: ops in loops, leaked bmesh, deprecated context dicts, per-vertex Python loops.

RuleScopeFlags
always-free-bmesh **/*.py Flag bmesh.new() calls without a paired bm.free() in a try/finally block. BMesh allocates C-side memory that Python's garbage collector cannot reclaim; missing free() leaks and eventually crashes Blender.
no-unapplied-modifiers-on-export **/*.py Flag an export call on objects that still carry unapplied modifiers when the export arguments do not request evaluated geometry. The engine then receives the authored cage, not the modifier result.
prefer-data-over-ops-in-loops **/*.py Flag bpy.ops.* calls inside iteration over many objects, meshes, or frames. Each bpy.ops call triggers a full depsgraph evaluation and UI redraw; loops slow down by orders of magnitude. Use bpy.data.* and bmesh instead.
prefer-temp-override-over-context-copy **/*.py Flag uses of bpy.context.copy() to override context for an operator call. The copy-and-pass pattern was deprecated in Blender 4.x and the override semantics were removed in 5.x. Use bpy.context.temp_override(**overrides) as a context manager instead.
target-extensions-platform-format **/__init__.py, **/blender_manifest.toml Flag new Blender add-ons that ship only a legacy bl_info dict without a blender_manifest.toml. New add-ons must use the Extensions Platform format. bl_info may appear alongside as a fallback for backward compatibility, but the manifest is the source of truth.
type-annotate-props-and-defend-context **/*.py Flag two related anti-patterns. (1) bpy.props defined as class-level assignments instead of type annotations (deprecated since 2.8). (2) Code that touches bpy.context.active_object without guarding for None.
use-correct-axis-rna-per-exporter **/*.py Flag export_scene.gltf calls that pass FBX axis_forward or axis_up, and export_scene.fbx calls that pass glTF export_yup. The two exporters do not share axis RNA.
use-foreach-set-for-bulk-data **/*.py Flag Python loops that set vertex coordinates, normals, UVs, or other bulk per-element data one element at a time. For meshes of more than a few thousand elements, this is 100x to 1000x slower than mesh.vertices.foreach_set("co", flat_array), which writes through to C-level storage in one pass.
validate-imported-mesh-scale **/*.py Flag a glTF or FBX import followed by mesh operations with no transform_apply and no unit-scale check. Generated files often arrive with non-identity object scale or a non-meter scene scale; mesh edits then bake the wrong size.
Snippets 27 patterns
Install

Clone it. Point your AI at it.

  1. Clone the repo: git clone https://github.com/TMHSDigital/Blender-Developer-Tools
  2. Cursor: copy rules/ into your project's .cursor/rules/ — they auto-apply via scope globs; reference skills by name in chat
  3. Claude Code: copy skills/ and rules/ into your project workspace, or point Claude Code at the checkout
  4. Grab snippets/ and templates/ as starting points for add-ons and headless batch jobs