> blender-animation
Animate 3D objects and characters in Blender with Python. Use when the user wants to keyframe properties, create armatures and rigs, set up IK/FK chains, animate shape keys for facial animation, edit F-Curves, use the NLA editor to blend actions, add drivers for expression-based animation, or script any animation workflow in Blender.
curl "https://skillshub.wtf/TerminalSkills/skills/blender-animation?format=md"Blender Animation
Overview
Create and control animations in Blender using Python. Keyframe object and bone transforms, build armatures with IK/FK rigs, animate shape keys for facial motion, edit F-Curves for timing control, layer actions in the NLA editor, and wire up drivers for expression-based automation — all scriptable from the terminal.
Instructions
1. Keyframe object properties
import bpy
obj = bpy.data.objects["Cube"]
# Keyframe location at multiple frames
for loc, frame in [((0,0,0), 1), ((5,0,3), 30), ((5,4,0), 60)]:
obj.location = loc
obj.keyframe_insert(data_path="location", frame=frame)
# Also works for rotation_euler, scale, single-axis (index=2 for Z), custom props
obj.rotation_euler = (0, 0, 3.14159)
obj.keyframe_insert(data_path="rotation_euler", frame=60)
obj.scale = (2, 2, 2)
obj.keyframe_insert(data_path="scale", frame=60)
obj["intensity"] = 1.0 # custom property
obj.keyframe_insert(data_path='["intensity"]', frame=30)
bpy.context.scene.frame_start, bpy.context.scene.frame_end = 1, 60
2. Control F-Curve interpolation and modifiers
import bpy
action = bpy.data.objects["Cube"].animation_data.action
for fcurve in action.fcurves:
for kp in fcurve.keyframe_points:
kp.interpolation = 'BEZIER' # CONSTANT, LINEAR, BEZIER, SINE, EXPO, BOUNCE, ELASTIC
kp.easing = 'EASE_IN_OUT' # AUTO, EASE_IN, EASE_OUT, EASE_IN_OUT
# Cycles modifier to loop the animation
mod = fcurve.modifiers.new(type='CYCLES')
mod.mode_before = 'REPEAT' # NONE, REPEAT, REPEAT_OFFSET, MIRROR
mod.mode_after = 'REPEAT'
# Add noise to a specific channel
z_curve = action.fcurves.find("location", index=2) # Z location
if z_curve:
noise = z_curve.modifiers.new(type='NOISE')
noise.strength, noise.scale = 0.3, 5.0
3. Create armatures and bones
import bpy
arm_data = bpy.data.armatures.new("Rig")
arm_obj = bpy.data.objects.new("Rig", arm_data)
bpy.context.collection.objects.link(arm_obj)
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='EDIT')
# (name, head, tail, parent_name, connected)
bone_defs = [
("Spine", (0,0,1.0), (0,0,1.4), None, False),
("Chest", (0,0,1.4), (0,0,1.8), "Spine", False),
("UpperArm.L", (0.2,0,1.7), (0.5,0,1.4), "Chest", False),
("Forearm.L", (0.5,0,1.4), (0.8,0,1.1), "UpperArm.L", True),
("Thigh.L", (0.1,0,1.0), (0.1,0,0.5), "Spine", False),
("Shin.L", (0.1,0,0.5), (0.1,0,0.05),"Thigh.L", True),
]
for name, head, tail, parent, connected in bone_defs:
b = arm_data.edit_bones.new(name)
b.head, b.tail = head, tail
if parent:
b.parent = arm_data.edit_bones[parent]
b.use_connect = connected
bpy.ops.object.mode_set(mode='OBJECT')
4. Set up constraints on bones
import bpy
arm_obj = bpy.data.objects["Rig"]
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='POSE')
# IK — use an empty as target, chain_count limits affected bones
ik = arm_obj.pose.bones["Forearm.L"].constraints.new('IK')
ik.target = bpy.data.objects["IK_Target"]
ik.pole_target = bpy.data.objects["IK_Pole"]
ik.chain_count = 2
# Copy Rotation — mirrors another object's rotation
cr = arm_obj.pose.bones["Chest"].constraints.new('COPY_ROTATION')
cr.target = bpy.data.objects["ChestCtrl"]
# Other types: LIMIT_ROTATION, DAMPED_TRACK, STRETCH_TO, COPY_LOCATION, TRACK_TO
bpy.ops.object.mode_set(mode='OBJECT')
5. Keyframe bone poses
import bpy, math
arm_obj = bpy.data.objects["Rig"]
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='POSE')
if not arm_obj.animation_data:
arm_obj.animation_data_create()
action = bpy.data.actions.new("WalkCycle")
arm_obj.animation_data.action = action
# Keyframe thigh swing: back → neutral → forward
thigh = arm_obj.pose.bones["Thigh.L"]
for angle, frame in [(-30, 1), (0, 13), (30, 25)]:
thigh.rotation_euler = (math.radians(angle), 0, 0)
thigh.keyframe_insert(data_path="rotation_euler", frame=frame)
bpy.ops.object.mode_set(mode='OBJECT')
6. Create and animate shape keys
import bpy
obj = bpy.data.objects["Head"]
mesh = obj.data
if not mesh.shape_keys: # Basis key required first
obj.shape_key_add(name="Basis", from_mix=False)
smile = obj.shape_key_add(name="Smile", from_mix=False)
for i, vert in enumerate(smile.data):
if i in [42, 43, 56, 57]: # mouth corner vertices
vert.co.z += 0.02
# Animate shape key value (0.0 → 1.0 → 0.0)
sk = mesh.shape_keys.key_blocks["Smile"]
for val, frame in [(0.0, 1), (1.0, 15), (0.0, 30)]:
sk.value = val
sk.keyframe_insert(data_path="value", frame=frame)
7. Layer actions with the NLA editor
import bpy
arm_obj = bpy.data.objects["Rig"]
if not arm_obj.animation_data:
arm_obj.animation_data_create()
# Push actions as NLA strips on separate tracks
track1 = arm_obj.animation_data.nla_tracks.new()
track1.name = "Walk"
strip1 = track1.strips.new("Walk", start=1, action=bpy.data.actions["WalkCycle"])
strip1.repeat = 4 # loop 4 times
strip1.blend_type = 'REPLACE' # REPLACE, COMBINE, ADD, SUBTRACT, MULTIPLY
track2 = arm_obj.animation_data.nla_tracks.new()
track2.name = "Wave"
strip2 = track2.strips.new("Wave", start=25, action=bpy.data.actions["WaveHand"])
strip2.blend_type = 'COMBINE' # blend on top of walk
strip2.influence = 0.8 # partial blend
arm_obj.animation_data.action = None # clear active action so NLA takes over
8. Add drivers for expression-based animation
import bpy
driver = bpy.data.objects["Cube"].driver_add("location", 2).driver # drive Z location
driver.type = 'SCRIPTED'
driver.expression = "sin(frame * 0.1) * 2"
# Add a variable that reads another object's transform
var = driver.variables.new()
var.name = "ctrl"
var.type = 'TRANSFORMS'
var.targets[0].id = bpy.data.objects["Controller"]
var.targets[0].transform_type = 'LOC_X'
var.targets[0].transform_space = 'WORLD_SPACE'
driver.expression = "ctrl * 3" # also works on shape keys via sk.driver_add("value")
Examples
Example 1: Bouncing ball with squash and stretch
User request: "Animate a ball bouncing with squash and stretch"
import bpy
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.5, location=(0, 0, 3))
ball = bpy.context.active_object
ball.name = "BouncingBall"
keyframes = [ # (frame, z_pos, scale_x, scale_y, scale_z)
(1, 3.0, 1.0, 1.0, 1.0), (12, 0.5, 1.0, 1.0, 1.0), # fall
(15, 0.3, 1.3, 1.3, 0.6), (18, 0.5, 0.85, 0.85, 1.2), # squash + stretch
(30, 2.2, 1.0, 1.0, 1.0), (42, 0.5, 1.0, 1.0, 1.0), # apex + second fall
(45, 0.3, 1.2, 1.2, 0.7), (55, 1.5, 1.0, 1.0, 1.0), # smaller bounce
(62, 0.5, 1.0, 1.0, 1.0), # settle
]
for frame, z, sx, sy, sz in keyframes:
ball.location = (0, 0, z)
ball.keyframe_insert(data_path="location", frame=frame)
ball.scale = (sx, sy, sz)
ball.keyframe_insert(data_path="scale", frame=frame)
for fc in ball.animation_data.action.fcurves:
for kp in fc.keyframe_points:
kp.interpolation = 'BEZIER'
bpy.context.scene.frame_end = 62
bpy.ops.wm.save_as_mainfile(filepath="/tmp/bouncing_ball.blend")
Example 2: Arm rig with IK reaching for an object
User request: "Create a simple arm rig with IK and animate it reaching for a cube"
import bpy
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
arm_data = bpy.data.armatures.new("ArmRig")
arm_obj = bpy.data.objects.new("ArmRig", arm_data)
bpy.context.collection.objects.link(arm_obj)
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='EDIT')
prev = None
for name, head, tail in [("UpperArm",(0,0,1.5),(0.6,0,1.5)),
("Forearm",(0.6,0,1.5),(1.2,0,1.5)),
("Hand",(1.2,0,1.5),(1.4,0,1.5))]:
b = arm_data.edit_bones.new(name)
b.head, b.tail = head, tail
if prev: b.parent, b.use_connect = prev, True
prev = b
bpy.ops.object.mode_set(mode='OBJECT')
# IK target + constraint
bpy.ops.object.empty_add(type='PLAIN_AXES', location=(1.2, 0, 1.5))
ik_target = bpy.context.active_object
ik_target.name = "IK_Hand"
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='POSE')
ik = arm_obj.pose.bones["Hand"].constraints.new('IK')
ik.target, ik.chain_count = ik_target, 3
bpy.ops.object.mode_set(mode='OBJECT')
# Animate IK target reaching toward a cube
bpy.ops.mesh.primitive_cube_add(size=0.3, location=(1.5, 0.5, 1.0))
for loc, frame in [((1.2,0,1.5),1), ((1.5,0.5,1.0),30), ((1.5,0.5,1.3),50)]:
ik_target.location = loc
ik_target.keyframe_insert(data_path="location", frame=frame)
for fc in ik_target.animation_data.action.fcurves:
for kp in fc.keyframe_points:
kp.interpolation, kp.easing = 'BEZIER', 'EASE_IN_OUT'
bpy.context.scene.frame_end = 50
bpy.ops.wm.save_as_mainfile(filepath="/tmp/arm_grab.blend")
Guidelines
data_pathmust match the RNA path exactly:"location","rotation_euler","scale",'["custom_prop"]'.- Armature workflow: Edit mode for bones (
edit_bones), Pose mode for constraints and keyframes (pose.bones). - For IK, use Empty objects as targets.
chain_count=0solves the entire chain to root. - Shape keys need a "Basis" key first. Animate via
key_blocks["Name"].value(0.0 to 1.0). - Interpolation:
CONSTANTfor hold/step,LINEARfor mechanical,BEZIERwith easing for organic motion. - Set
animation_data.action = Noneto let NLA strips drive — an active action overrides NLA. - For walk cycles, keyframe one stride and use a Cycles F-Curve modifier or NLA repeat to loop.
- Bake constraints/drivers with
bpy.ops.nla.bake()before export — other software cannot read them.
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zoho
Integrate and automate Zoho products. Use when a user asks to work with Zoho CRM, Zoho Books, Zoho Desk, Zoho Projects, Zoho Mail, or Zoho Creator, build custom integrations via Zoho APIs, automate workflows with Deluge scripting, sync data between Zoho apps and external systems, manage leads and deals, automate invoicing, build custom Zoho Creator apps, set up webhooks, or manage Zoho organization settings. Covers Zoho CRM, Books, Desk, Projects, Creator, and cross-product integrations.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> zipkin
Deploy and configure Zipkin for distributed tracing and request flow visualization. Use when a user needs to set up trace collection, instrument Java/Spring or other services with Zipkin, analyze service dependencies, or configure storage backends for trace data.