import bpy
bpy.app.version_string # current blender version'5.3.0 Beta'
import sys
from pathlib import Path
# The kernel runs inside Blender (not in the notebook folder), so add the docs helpers to sys.path
sys.path.append(str(Path.home() / "projects/bpy-gallery/docs/utils"))
from gallery_utils import ASSETS, fresh_scene, render_result
# Set render resolution
bpy.context.scene.render.resolution_x = 500
bpy.context.scene.render.resolution_y = 200



bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
cube = bpy.context.active_object
bevel_modifier = cube.modifiers.new(name="Bevel", type='BEVEL')
bevel_modifier.width = 0.4 # Adjust the bevel width
bevel_modifier.segments = 5 # Number of segments for a smoother bevel
# Optionally apply the modifier if needed (not necessary for visualization)
# bpy.ops.object.modifier_apply(modifier="Bevel")
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
cube = bpy.context.active_object
# Create the subdivision surface modifier and change it to 'SIMPLE'
subsurf_modifier = cube.modifiers.new(name="Subdivision", type='SUBSURF')
subsurf_modifier.subdivision_type = 'SIMPLE' # Change to 'SIMPLE' instead of 'CATMULL_CLARK'
subsurf_modifier.levels = 4 # Viewport subdivision level
subsurf_modifier.render_levels = 4 # Render subdivision level
displace_modifier = cube.modifiers.new(name="Displace", type='DISPLACE')
displace_modifier.strength = 0.5 # Adjust the displacement strength
texture = bpy.data.textures.new("DisplaceTexture", type='CLOUDS')
texture.noise_scale = 1 # Reduce the noise scale to make it more detailed
displace_modifier.texture = texture # Assign the texture to the modifier
# Add a cube
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
cube = bpy.context.active_object
# Create a green material
mat = bpy.data.materials.new(name="GreenMaterial")
mat.diffuse_color = (0, 1, 0, 1) # viewport color only (Solid mode)
mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (0, 1, 0, 1) # render color
# Apply material to the cube
cube.data.materials.clear() # Clear any existing materials
cube.data.materials.append(mat)
# colors with nodes
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
cube = bpy.context.active_object
# Create and assign a blue material to the cube
mat = bpy.data.materials.new(name="BlueMaterial")
# mat.use_nodes = True # outdated: materials always use nodes since Blender 5.0
bsdf = mat.node_tree.nodes.get('Principled BSDF')
bsdf.inputs['Base Color'].default_value = (0, 0, 1, 1) # Blue color (R, G, B, A)
# Apply material to the cube
cube.data.materials.clear() # Clear any existing materials
cube.data.materials.append(mat)
bpy.ops.mesh.primitive_plane_add(size=6, location=(0, 0, 0))
# plane = bpy.context.object # only blender 4.1
plane = bpy.context.active_object #blender 4.3
material = bpy.data.materials.new(name="ImageMaterial")
# material.use_nodes = True # outdated: materials always use nodes since Blender 5.0
bsdf = material.node_tree.nodes["Principled BSDF"]
tex_image = material.node_tree.nodes.new('ShaderNodeTexImage')
tex_image.image = bpy.data.images.load(str(ASSETS / "cute_dog.jpg"))
material.node_tree.links.new(bsdf.inputs['Base Color'], tex_image.outputs['Color'])
plane.data.materials.append(material)
Node trees can be written as code with nodebpy. Install it once into Blender’s Python with pip:
Requirement already satisfied: nodebpy in ./Applications/Blender.app/Contents/Resources/5.3/python/lib/python3.13/site-packages (520.35.1)
[notice] A new release of pip is available: 26.0.1 -> 26.2.1 [notice] To update, run: /Applications/Blender.app/Contents/Resources/5.3/python/bin/python3.13 -m pip install --upgrade pip
0
This builds the following shader tree:

from nodebpy import shader as s
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
cube = bpy.context.active_object
with s.material("YellowToOrangeGradient") as tree:
_ = (
s.GradientTexture(gradient_type="LINEAR")
>> s.ColorRamp(items=[(0.0, (1, 1, 0, 1)), (1.0, (1, 0.3, 0, 1))]) # yellow -> orange
>> s.PrincipledBSDF()
>> s.MaterialOutput()
)
cube.data.materials.clear()
cube.data.materials.append(tree.material)
The same works for geometry node trees:
from nodebpy import geometry as g
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
with g.tree("AnotherTree", collapse=True, arrange="sugiyama") as tree:
rotation = (
g.RandomValue.vector(min=-1, seed=2)
>> g.AlignRotationToVector()
>> g.RotateRotation(rotate_by=g.AxisAngleToRotation(angle=0.3))
)
_ = (
tree.inputs.integer("Count", 10)
>> g.Points(position=g.RandomValue.vector(min=-1))
>> g.InstanceOnPoints(instance=g.Cube(), rotation=rotation)
>> g.SetPosition(
position=g.Position() * 2.0 + (0, 0.2, 0.3),
offset=(0, 0, 0.1),
)
>> g.RealizeInstances()
>> g.InstanceOnPoints(g.Cube(), instance=...)
>> tree.outputs.geometry("Instances")
)
# Apply to active object
obj = bpy.context.active_object
modifier = obj.modifiers.new(name="AnotherTree", type="NODES")
modifier.node_group = bpy.data.node_groups["AnotherTree"]
You can any time also save your Blender file like this:
#bpy.ops.wm.open_mainfile(filepath="donut.blend") # this will loose kernel connection
filepath = str(ASSETS / "donut.blend")
# Import all objects from the .blend file
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
data_to.objects = data_from.objects
# Link imported objects to the current scene
for obj in data_to.objects:
if obj is not None:
bpy.context.collection.objects.link(obj)
# Render through the camera that came with donut.blend
bpy.context.scene.camera = next(obj for obj in data_to.objects if obj.type == 'CAMERA')
