Tail chains. Hair chains. Cape bones. Every time I finish blocking out the primary motion I spend another 20 minutes in the graph editor sliding keys forward on each bone. bone_02 gets +2, bone_03 gets +4, and so on. It works. It's also incredibly tedious when the chain is 8+ bones long.
So I wrote a script. Point it at a root bone, it walks the full hierarchy and offsets all keyframes by frames_per_level * depth. Root stays where it is, first-level children shift by +2, their children by +4, etc. Handles get moved along with the key positions so your bezier curves don't come out distorted.
import bpy
def offset_overlap_chain(armature_obj, root_bone_name, frames_per_level=2):
action = armature_obj.animation_data.action
if not action:
print("No active action found on this object.")
return
def walk(bone_name, depth):
if depth > 0:
offset = depth * frames_per_level
for fc in action.fcurves:
if f'pose.bones["{bone_name}"]' in fc.data_path:
for kp in fc.keyframe_points:
kp.co.x += offset
kp.handle_left.x += offset
kp.handle_right.x += offset
bone = armature_obj.data.bones.get(bone_name)
if bone:
for child in bone.children:
walk(child.name, depth + 1)
walk(root_bone_name, 0)
for fc in action.fcurves:
fc.update()
print(f"Chain offset complete. Root: '{root_bone_name}', {frames_per_level}f per level.")
# run with your armature selected
obj = bpy.context.active_object
offset_overlap_chain(obj, "tail_01", frames_per_level=2)A few things to know before you run it:
What I want to add next: a falloff curve so the per-level offset isn't strictly linear, and a direction flag to offset backward for anticipation instead of forward for follow-through. The destructive key editing makes me nervous, so I always duplicate the action first. Anyone done something like this non-destructively? Thinking maybe NLA offset strips could work but I haven't gone down that road yet. Curious how people are handling chain overlap beyond just manually dragging keys.