Skip to content

Latest commit

 

History

History
512 lines (393 loc) · 24.8 KB

File metadata and controls

512 lines (393 loc) · 24.8 KB

Blender Developer Tools


Skills, rules, snippets, templates, and runnable examples for Blender Python development

Release License

Validate Blender smoke tests Drift check

12 skills  •  6 rules  •  2 templates  •  17 snippets  •  22 examples

Examples Gallery  •  Quick start  •  Examples  •  Skills  •  Rules  •  Templates  •  Snippets  •  Roadmap


Overview

This repository ships 12 skills, 6 rules, 2 templates, 17 snippets, and 22 runnable examples for Blender Python development targeting Blender 5.1 (current stable) with Blender 4.5 LTS fallback support.

The content is consumed by AI coding agents (Cursor, Claude Code, any MCP-capable client) when working on Blender add-ons, geometry nodes scripts, batch pipelines, or animation tooling. There is no build step. Edit the markdown and Python files directly.

Layer Role
Skills Guided workflows: scaffolding, operators, panels, properties, mesh and bmesh, headless batch, slotted actions, geometry nodes, procedural materials, depsgraph queries, drivers and handlers, bl_info migration
Rules Guardrails for the most common AI mistakes: ops-in-loops, bmesh leaks, legacy bl_info only, prop assignments, deprecated context-copy override, per-element loops over bulk mesh data
Templates A working Extensions Platform add-on starter and a headless batch script starter
Snippets 17 small standalone Python files demonstrating canonical patterns

Quick start

git clone https://github.com/TMHSDigital/Blender-Developer-Tools.git
  • Cursor — point Cursor at the checkout (or symlink rules/ into your project). The .mdc rules apply automatically by glob scope; skills are referenced by name in chat.
  • Claude Code — copy skills/ and rules/ into your project workspace, or keep this repo as a checkout that Claude Code references directly.
  • Run an example — every example is a self-checking headless script (exit non-zero on failure, no GPU needed for the check):
blender --background --python examples/bmesh-gear/bmesh_gear.py --

Supported Blender versions

Version Status
Blender 5.1.x Primary target (all examples assume 5.1)
Blender 4.5 LTS Fallback supported (skills show both code paths where 4.x and 5.x APIs diverge)
Blender 5.2 LTS Sweep planned for July 2026 (see ROADMAP.md)

Examples

Runnable, smoke-gated demos live in examples/ — each is executed headless on both Blender 4.5 LTS and 5.1 by the blender-smoke workflow, so the screenshots reflect code that actually runs. Browse them all with filters and full-size renders in the examples gallery, or expand a category below.

Grease pencil rosette: five nested neon rose curves glowing against a dark studio wall Parent inverse orrery: a brass tabletop orrery with a glowing sun and three planets Compositor glare: three neon rings with colored bloom halos Image pixels testcard: a studio monitor showing a procedural broadcast test card

Materials, shading & compositing — 6 examples
Swatch grid: a three-by-two grid of shaded spheres - polished gold and brushed copper metals, red and blue plastics, an emissive orange, and a matte white - against a dark studio backdrop, rendered with EEVEE

A procedural-materials swatch grid — Principled metal and dielectric, the emission pattern, and the cross-version set_specular shim. Doubles as a live proof of the EEVEE engine-id mapping (BLENDER_EEVEE on 5.x, BLENDER_EEVEE_NEXT on 4.2-4.5).

Shader node group: a teal sphere and a magenta sphere sharing one TintedGloss node group with different Tint parameters

One reusable TintedGloss group declared via tree.interface.new_socket, instanced in two materials with different Tint values. Witnesses the grouping contract: shared datablock (users == 2), parameters on the group node — two spheres, one group, two colors.

Compositor glare: three neon rings - violet, cyan, and amber - with colored bloom halos and mirrored reflections on a dark studio floor

Bloom through the compositor on both sides of the 5.0 rewrite — a Glare (Fog Glow) node fed by Render Layers, wired via scene.compositing_node_group on 5.x and scene.node_tree on 4.x. Witnesses with pixels that the halo falls off strictly with the compositor on and is exactly zero with it off — and that EEVEE has no use_bloom.

Color attribute wheel: an HSV color wheel disc with a white center fading into a vivid rainbow rim, leaning upright toward the camera on a dark studio floor with a warm light pool behind it

The modern color-attributes API — mesh.color_attributes.new() on the CORNER domain, not the deprecated vertex_colors alias, filled by expanding per-vertex HSV across face corners with foreach_get/foreach_set. Asserts the attribute is sized to loop count (not vertex count), is active_color, and that the shader Attribute node is actually linked to Base Color.

Image pixels testcard: a studio monitor showing a procedural broadcast test card — seven neon color bars behind the classic dark circle, a luminance ramp, and a PLUGE row with a white bottom-left origin marker — over a teal underglow on a dark studio floor

A procedural broadcast test card written into bpy.data.images.new() with one pixels.foreach_set() call. Asserts the buffer is always flat RGBA (channels == 4 even with alpha=False), that byte storage quantizes at exactly ≤ 0.5/255 and strictly > 0 while float_buffer=True round-trips at float32 precision, that scale() reallocates (stale-size reads raise), and the save() trap: source silently flips to FILE, the buffer drops, and later pixels reads come from whatever sits on disk — proven with an imposter file. save_render() is the non-destructive path.

PNG vs EXR alpha: two easel panels on a dark studio floor — left float→PNG false-unpremul clamps dark mid-tones to white at low alpha, right the EXR-clean authored straight buffer with surviving red and green primaries

float_buffer=True images saved to PNG are written as RGBA16 and unpremultiplied as if associated-alpha — straight-authored dark values at low alpha clamp to white (closed-form error 0.98 at (0.02, a=1/255)). OpenEXR preserves float RGBA; byte images stay straight 8-bit. Also witnesses EXR color_mode='RGB' dropping alpha.

Bmesh gear: a machined brass 14-tooth gear with concentric turning marks catching the light, tilted at a three-quarter angle on a dark studio floor

A 14-tooth gear built entirely with bmesh — with bm.free() in a try/finally, as the ownership contract demands. Asserts the closed-form vert/edge/face counts and that the result is watertight (every edge borders exactly two faces).

UV layer grid: two easel panels on a dark studio floor — left a flat teal square from a missing UV layer sampling texel (0,0), right a magenta/cyan neon checker proving pre-create + calc_uvs

bmesh.ops.create_grid(..., calc_uvs=True) is a silent no-op unless a UV layer already exists — without one an Image Texture samples texel (0, 0) everywhere. Asserts the hazard, the pre-create repair against closed-form grid UVs, and an explicit assignment fallback.

Wave displace: a glossy sapphire-blue surface displaced into smooth standing-wave dunes against a black backdrop

Bulk vertex IO at real scale — 9,409 vertices displaced into a standing wave with one foreach_get and one foreach_set, no per-vertex access. Asserts the count is unchanged, the Z span matches the amplitude, and a probe vertex matches the closed-form wave.

Shape-key blend: three violet blocks on a dark studio floor showing a relative Tall shape key at values 0, 0.25, and 0.5 - plain cube to lifted, flared truncated pyramid, left to right

A relative Tall shape key that lifts and flares the top face — authored through shape_key_add / key_blocks / .value. Witnesses that shape keys do not rewrite mesh.vertices: every evaluated vert matches basis + value × (key − basis).

Curve bevel arc: a rose beveled Bezier semicircle tube resting on a dark studio floor

A beveled Bezier semicircle authored on bpy.types.Curvesplines.new('BEZIER'), bezier_points, bevel_depth, use_fill_caps — so the curve renders as a solid tube without a prior mesh conversion. Asserts eight points, bevel_depth == 0.15, and evaluated topology 1044 verts / 1028 faces.

Text version stamp: beveled brass 5.1.2 numerals standing on a dark studio floor over a glowing teal underline, with a small steel BLENDER caption above — the body text is the live bpy.app.version_string

A beveled 3D stamp of the running Blender version — a TextCurve whose body is the live bpy.app.version_string, so every render self-documents which Blender made it. Asserts the TextCurve solids closed form (evaluated z-extent = 2 × (extrude + bevel_depth), bevel widening the outline by 2 × bevel_depth), that flat text is filled but planar, that body edits regenerate geometry, that version_string is not bare semver on LTS builds ("4.5.11 LTS"), and that a Mesh reference dies at to_mesh_clear().

Depsgraph-evaluated export: a teal base cube beside the smooth teal sphere of its subdivision-surface evaluated form - one shared material, two silhouettes - on a dark studio floor with a warm light pool behind

A depsgraph-evaluated export — builds a cube with SUBSURF, measures the evaluated mesh via evaluated_get().to_mesh() / to_mesh_clear(), and asserts wm.obj_export ships the modifier-applied geometry (exported vertex count == evaluated > base).

Geometry Nodes — 2 examples
Geometry Nodes SDF remesh: a crimson ceramic torus with visible voxel-remesh facets, resting on a dark studio floor

A Geometry Nodes SDF remesh (MeshToSDFGridGridToMesh at the SDF zero-level). Witnesses the fix that an SDF grid is meshed with Grid to Mesh, not Volume to Mesh, and that a Set Material node carries the material through the remesh.

GN instance grid: nine lime-green cubes in a 3x3 grid on a dark studio floor, instanced via Geometry Nodes Instance on Points

A generative Geometry Nodes tree — Mesh Grid → Instance on Points → Realize Instances — attached as a NODES modifier with no Group Input. Asserts evaluated topology is verts = 72, faces = 54, and Set Material carries the lime accent.

Animation, rigging & constraints — 5 examples
Turntable: a copper Suzanne mid-rotation on a dark studio floor, rendered with EEVEE

A slotted-actions Z-rotation turntable keyed through the cross-version channelbag path (get_channelbag_for_slot). Witnesses the slotted-actions fix: ensure-helper channelbag on 5.x, strip.channelbag on 4.4/4.5.

Driver wave: sixteen orange columns whose heights form a sine skyline on a dark studio floor, each driven by a driver_namespace function

A driver_namespace function driving sixteen column heights through SCRIPTED drivers. Witnesses the evaluation contract: driven values appear after a view-layer update on the evaluated copy and the flushed-back original, and both must match the closed form.

Damped Track aim: brass spikes in a tilted cage around an ember core on a dark studio plinth, each tip constrained toward the center

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. Asserts twelve unmuted Damped Track constraints and evaluated local +Z alignment toward the core (dot ≥ 0.998).

Armature bend: three tapered tubes on a dark studio floor showing rest, half, and full curl under a four-bone armature, per-bone weight bands blending teal through amber to coral at the joints

A four-bone chain built with edit_bones skins a tapered tube through name-bound vertex groups, posed into a curl and read back through the depsgraph. Asserts that edit_bones is empty outside edit mode, and that the armature modifier is exactly linear blend skinning — every evaluated vertex equals Σ wᵢ · (pose_bone.matrix @ bone.matrix_local.inverted()) @ rest, with the root ring pinned and the tip deflected. A straight tube is a failure.

Parent inverse orrery: a brass tabletop orrery with a glowing sun, three planets on pivot arms inside brass orbit rings, and a silver moon, on a dark studio floor

A brass orrery parented entirely through the data API — the keep-world idiom child.parent = pivot; child.matrix_parent_inverse = pivot.matrix_world.inverted() carries arms, planets, and a two-level moon through spinning pivots. Asserts bare .parent = really teleports, matrix_world is stale until view_layer.update(), and every orbit lands on its closed form.

Context & Grease Pencil — 2 examples
Temp-override join: an amber three-step staircase of joined unit cubes on a dark studio floor

Three unit cubes joined into a staircase under bpy.context.temp_override — the supported replacement for the removed context.copy() dict-pass form. Asserts one mesh remains, sources are gone, and local Z spans all three steps.

Grease pencil rosette: five nested neon rose curves drawn as tapered Grease Pencil v3 strokes, cyan through magenta to red, glowing against a dark studio wall with a soft blue halo

Five nested rose curves drawn with the Grease Pencil v3 attribute API — layer → frames.new(1).drawingadd_strokes → per-point position, radius, opacity, and vertex color. Asserts the GPv3 address break: on 4.5 GPv3 is grease_pencils_v3 while grease_pencils is still legacy; on 5.x legacy is gone and GPv3 owns the name. Point writes lazily materialize attribute layers, and every position round-trips through the raw POINT buffer.

How content is organized

skills/<name>/SKILL.md   - 12 skill files, YAML frontmatter, one canonical pattern each
rules/<name>.mdc         - 6 rule files, anti-pattern + correction
templates/<name>/        - 2 template directories (extension-addon-template, headless-batch-script-template)
snippets/<name>.py       - 17 standalone Python snippets, 5 to 50 lines each

Using rules in Cursor

The .mdc files in rules/ apply automatically when Cursor opens a Blender Python project, scoped by the globs in each rule's frontmatter. The six rules are:

  • prefer-data-over-ops-in-loops: flags bpy.ops.* calls inside object iteration
  • always-free-bmesh: flags bmesh.new() without paired bm.free() in try/finally
  • target-extensions-platform-format: flags add-ons missing blender_manifest.toml
  • type-annotate-props-and-defend-context: flags bpy.props assignment form and unguarded context.active_object
  • prefer-temp-override-over-context-copy: flags bpy.context.copy() passed to operators (deprecated 4.x, removed 5.x)
  • use-foreach-set-for-bulk-data: flags Python loops over mesh.vertices setting co, normals, or other per-element bulk data

Symlink or clone this repo, then point Cursor at it as a skills/rules source.

Using the templates

templates/extension-addon-template/ is a working Blender extension. Copy the directory, edit blender_manifest.toml (id, version, name, maintainer), and install via Edit > Preferences > Get Extensions > Install From Disk. The template registers an Operator, a Panel, and a PropertyGroup, and demonstrates the register_classes_factory pattern with symmetric register() and unregister().

templates/headless-batch-script-template/ is a working starter for unattended Blender batch jobs. It opens a .blend, optionally adds and applies a modifier to every mesh, and exports to glTF, with explicit exit codes for CI integration. Run with blender --background <input.blend> --python script.py -- --output ....

Snippets

Each snippet is a standalone Python file under snippets/. They are not loaded as a package. Open one, copy the relevant lines into your script, and adapt the names. Each file's header comment cites the Blender doc URL or research section the pattern came from.

Canonical references

Resource Use it for
Blender 5.1 Python API Authoritative reference for current stable APIs
Blender 4.5 LTS Python API LTS reference when targeting 4.5
Extensions Platform manual blender_manifest.toml schema, hosting, install flow
developer.blender.org Release notes, breaking change tracking, design docs

When community content (Stack Overflow, older add-on source) conflicts with the official docs, prefer the docs. The 2.x to 4.x to 5.x churn around Actions, Extensions, and property handling has invalidated a lot of older material.

Roadmap

See ROADMAP.md for the candidate pool and what ships next — including the planned Blender 5.2 LTS sweep (July 2026). Releases are cut automatically from conventional commits; the full history lives in CHANGELOG.md.

Contributing

Issues and pull requests are welcome — see CONTRIBUTING.md for the workflow, SECURITY.md for reporting vulnerabilities, and CODE_OF_CONDUCT.md for community standards. New examples must follow the anatomy of examples/bmesh-gear/ and the render look in docs/VISUAL-STYLE.md.

License

Copyright (c) 2026 TM Hospitality Strategies. Licensed under CC-BY-NC-ND-4.0.