I have two objects with identical topology and shape keys. Copying over the static shape key values is simple enough.

def copy_shape_keys(source_obj_name, target_obj_name):
source_obj = bpy.data.objects.get(source_obj_name)
target_obj = bpy.data.objects.get(target_obj_name)    
source_shape_keys = source_obj.data.shape_keys
target_shape_keys = target_obj.data.shape_keys
for source_key_block in source_shape_keys.key_blocks:
    target_key_block = target_shape_keys.key_blocks.get(source_key_block.name)
    target_key_block.value = source_key_block.value

How could this be extended to include copying over the keyframes of animated shape keys? Blenders API does not seem to provide the necessary access to shape key animation data for this to be feasible.

None of the answers from this thread directly address this question.

  • $\begingroup$ Hello and welcome. by "animated shape keys" do you just mean the keyframes as well? $\endgroup$ Aug 6, 2023 at 16:09
  • $\begingroup$ @HarryMcKenzie Hello, thank you. Yes, I would like to copy the keyframes of one shape key to another. Doing the animation copying through keyframes is the most straight forward approach, though I am open to other methods of copying over animation data, such as through actions for example. $\endgroup$ Aug 6, 2023 at 16:22

1 Answer 1


Here's the script that will copy the shape keys and the keyframe animation as well:

import bpy

def copy_shape_keys(src, tgt):
    if len(src.data.vertices) != len(tgt.data.vertices):
        raise RuntimeError('Objects have different vertex count!')

    if tgt.data.shape_keys:
        for key in tgt.data.shape_keys.key_blocks:

    keys = src.data.shape_keys

    for key in keys.key_blocks:
        k = tgt.shape_key_add(name=key.name, from_mix=False)
        for i, v in enumerate(src.data.vertices):
            k.data[i].co = key.data[i].co
        k.value = key.value
        data = tgt.data.shape_keys.animation_data_create()
        data.action = bpy.data.actions.new(name=key.name+"_action")
        for fc in keys.animation_data.action.fcurves:
            tgt_fc = data.action.fcurves.new(data_path=fc.data_path)
            for kf in fc.keyframe_points:
                tgt_fc.keyframe_points.insert(kf.co.x, kf.co.y)

source_obj = bpy.data.objects.get("Cube 1")
target_obj = bpy.data.objects.get("Cube 2")

copy_shape_keys(source_obj, target_obj)
  • $\begingroup$ This script works in the example file, thank you! I am aware that this is technically outside of the scope of my clarification; but could you adjust this script so that it can handle copying over the animation data for all animated shape keys? Say if "Key 2" on "Cube 1" had keyframes as well. $\endgroup$ Aug 8, 2023 at 20:06
  • 1
    $\begingroup$ whoops made a mistake. now its fixed. $\endgroup$ Aug 8, 2023 at 23:18
  • $\begingroup$ Thank you very much. In my use case, I sometimes have models that don't have exactly matching vertex counts or even topology. They only have matching shape key names (for facial expressions). Is there a way to copy animated shape keys, that doesn't rely on matching vertex count and topology? I am aware that this is a flaw of how my question was formulated. I did not expect the solution to involve deleting and rebuilding of shape keys using exact vertex data. $\endgroup$ Aug 8, 2023 at 23:59
  • $\begingroup$ you can remove the check and see if it still works. but take note that you cannot have a basis shape key with 10 vertices and then a key 1 shape key with 11 vertices. $\endgroup$ Aug 9, 2023 at 0:33
  • $\begingroup$ Removing the check and running it on meshes with different vertex counts results in all shape keys being deleted and turns the base shape into a mangled mess. If the target mesh has less vertices than the source it raises an index out of range IndexError in line 16. If it has more, it's a 'NoneType' object has no attribute 'action' AttributeError in line 22. Is there really not a more straight forward method to access shape key animation data? $\endgroup$ Aug 9, 2023 at 1:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .