| name | blender-style-generative |
| description | Parametric / instanced generative geometry style for Blender music videos — geometry-nodes instancing, growth, tessellation, fields of forms that evolve and react. Use for abstract/evolving music (Floating Points, Kiasmos, Skee Mask) and as the cure for the slow bespoke-mesh build. Built on tools/blender_style_kit.py; richer engines: Sverchok / Animation Nodes / Sorcar / GrowthNodes / Tissue. See [[blender-styles]]. |
blender-style: Generative
Forms generated by rule, not hand-placed — grids/fields of instances, growth, tessellation — evolving and reacting to the music. Also the performance fix: instancing renders thousands of copies that cost almost nothing to build (vs dawn's thousands of unique meshes → 10-min scene build).
Visual DNA
- Instances, not unique meshes. One base mesh, scattered on points via Geometry Nodes
Instance on Points. Build-time stays flat as count climbs.
- Parametric fields. Grids, spirals, point clouds, lattices — driven by math, perturbed by noise.
- Evolution over time. Density/scale/rotation/colour move with the song; the field is never static.
- Restraint in palette, complexity in form — let the geometry be the subject; keep colour simple.
Recipe: geometry-nodes instancing, reactive (kit-native)
import os, sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import blender_style_kit as kit
import bpy, math
def reactive_instance_field(name="field", n=46, spacing=1.4):
"""Grid of instanced cubes via Geometry Nodes; Scale/Spin inputs the reactor drives."""
bpy.ops.mesh.primitive_plane_add(size=1); host = bpy.context.object; host.name = name
ng = bpy.data.node_groups.new(name + " GN", "GeometryNodeTree")
ng.interface.new_socket("Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
ng.interface.new_socket("Scale", in_out="INPUT", socket_type="NodeSocketFloat").default_value = 0.3
ng.interface.new_socket("Spin", in_out="INPUT", socket_type="NodeSocketFloat")
nin = ng.nodes.new("NodeGroupInput"); nout = ng.nodes.new("NodeGroupOutput")
grid = ng.nodes.new("GeometryNodeMeshGrid")
grid.inputs["Size X"].default_value = grid.inputs["Size Y"].default_value = n * spacing
grid.inputs["Vertices X"].default_value = grid.inputs["Vertices Y"].default_value = n
cube = ng.nodes.new("GeometryNodeMeshCube"); cube.inputs["Size"].default_value = (0.6, 0.6, 0.6)
iop = ng.nodes.new("GeometryNodeInstanceOnPoints")
noise = ng.nodes.new("ShaderNodeTexNoise"); noise.inputs["Scale"].default_value = 1.5
pos = ng.nodes.new("GeometryNodeInputPosition")
scl = ng.nodes.new("GeometryNodeScaleInstances"); rot = ng.nodes.new("GeometryNodeRotateInstances")
mul = ng.nodes.new("ShaderNodeMath"); mul.operation = "MULTIPLY"
cz = ng.nodes.new("ShaderNodeCombineXYZ")
L = ng.links.new
L(grid.outputs["Mesh"], iop.inputs["Points"]); L(cube.outputs["Mesh"], iop.inputs["Instance"])
L(iop.outputs["Instances"], scl.inputs["Instances"])
L(pos.outputs["Position"], noise.inputs["Vector"])
L(noise.outputs["Fac"], mul.inputs[0]); L(nin.outputs["Scale"], mul.inputs[1])
L(mul.outputs[0], scl.inputs["Scale"])
L(scl.outputs["Instances"], rot.inputs["Instances"])
L(nin.outputs["Spin"], cz.inputs["Z"]); L(cz.outputs["Vector"], rot.inputs["Rotation"])
L(rot.outputs["Instances"], nout.inputs["Geometry"])
m = host.modifiers.new(name + " mod", "NODES"); m.node_group = ng
return host, m
def build(scene, args, features, rng):
kit.setup_render(scene, args, view_transform="Standard", look="Medium High Contrast", exposure=0.2)
kit.dark_world(scene, color=(0.01, 0.01, 0.03), strength=1.0)
bpy.ops.object.light_add(type="SUN", rotation=(math.radians(55), 0, math.radians(-30)))
bpy.context.object.data.energy = 3.5
glow = kit.emission_mat("field glow", (0.4, 0.7, 1.0), strength=1.5)
host, mod = reactive_instance_field("audio field", n=46); host.data.materials.append(glow)
bpy.ops.object.camera_add(location=(0, 26, 16)); cam = bpy.context.object
scene.camera = cam; cam.data.lens = 28; kit.look_at(cam, (0, 0, 0))
ids = {s.name: s.identifier for s in mod.node_group.interface.items_tree if s.item_type == "SOCKET"}
def react(ft, progress, frame):
mod[ids["Scale"]] = 0.25 + 1.4 * ft["bass"] + 0.8 * ft["flux"]
mod[ids["Spin"]] = progress * math.tau + 3.0 * ft["mid"]
host.update_tag()
kit.set_emission(glow, 1.0 + 3.0 * ft["rms"] + 4.0 * ft["high"])
cam.location = (18 * math.sin(progress * math.tau * 0.5), 26, 16 + 4 * ft["rms"])
kit.look_at(cam, (0, 0, 0))
return react
kit.run(build)
(Geometry-Nodes modifier inputs are addressed by socket identifier — mod["Socket_2"] — so resolve them by name from node_group.interface once, then drive per frame + host.update_tag().)
Runnable + verified
tools/blender_style_generative.py — complete runnable script. Render-verified (Blender 5.1, 320×180): an instanced cube field at noise+beat-driven heights, glowing blue, receding to camera — read cleanly first pass (the strongest-reading of the five at low res). Confirms the geometry-nodes instancing + driving inputs by socket identifier (mod[ids["Scale"]]) + host.update_tag() per frame all work offline.
Richer engines
Drive their outputs from the kit's reactor (set node inputs / object attributes per frame), or bake geometry and animate camera/emission.
When to use
Abstract, evolving, hypnotic, "the form is the subject" — Floating Points, Kiasmos, Skee Mask, IDM. Also reach here whenever a bespoke scene is too slow to build (instance instead). Not for narrative/world looks (→ [[blender-style-ps1]]). See [[blender-geometry-nodes]] for nodes, [[blender-3d-concepts]] for composition; verify with [[blender-video-iteration]].