I am making a piano song animation in cycles where in exact places (if a piano button was hit) the piano string lights up (Emission shader Strength = 5) then fades away (so go back to 0 Strength in 100 frames).

Until now I did this manually. So I checked the frame number where it should be lighten up let's say frame 2345, went back two frames (to 2343) set a keyframe (if this string was hit within 100 frames back its higher than 0) go back to original frame (2345) set the material strength to 5 and keyframe it then go forward a hundred frames (to 2445) set the Emission shader's Strength to 0 and made it keyframe. This is a really time consuming work especially now where there is 3-4 key is hit in the same time...

I need a really simple script to speed things up a little bit. My plan is to do the following workflow. I just check the frame number where the string should lighten up go to that frame in editor, select the string object what is should be lighten up, then hit "Run Script" and the script makes all this for me on the actual frame number on the selected object's material.

I want something like this:

import everything what is needed for this script

curMat = get active object's (what is actually selected in 3d editor) material  
curFrame = get the current frame which is shown in the timeline editor

keyframe_insert(curMat.Strength, curFrame-2)

for (curFrame-1 to curFrame+100)

check for keyframes in material strength and if there is



set strength(curMat, value = 5, curFrame)

keyframe_insert(curMat.Strength, curFrame)

set strenght(curMat, value = 0, curFrame+100)

keyframe_insert(curMat.Strength, curFrame+100)

I hope one of you can understand what I want to achieve and help me out with a few lines of code, I would really appreciate your help!


1 Answer 1


Haven't had time to test this too much, but should work (or be a good start if not):

import bpy

curMat   = bpy.context.object.active_material
curFrame = bpy.context.scene.frame_current

emitStr = curMat.node_tree.nodes['Emission'].inputs[1]

emitStr.keyframe_insert( 'default_value', frame = curFrame - 2 )

# Delete existing keyframes from curFrame -1 to curFrame + 100
fcurves = curMat.node_tree.animation_data.action.fcurves
for fc in fcurves:
    if fc.data_path == 'nodes["Emission"].inputs[1].default_value':
        kfs     = set( [ kf.co[0] for kf in fc.keyframe_points ] )
        kfRange = set( range( curFrame - 1, curFrame + 100 ) )
        kfsInRange = kfs & kfRange # Inresect both sets

        for i in kfsInRange: 
            emitStr.keyframe_delete( 'default_value', frame = i )

emitStr.default_value = 5                  # Set strength to 5
emitStr.keyframe_insert( 'default_value' ) # Sets kf in current frame by default

emitStr.default_value = 0
emitStr.keyframe_insert( 'default_value', frame = curFrame + 100 )
  • $\begingroup$ Dear TLousky! Thanks for your quick answer. It seems to be good, but not perfect yet. When I run this script there is an error on line 11. Console says: AttributeError: 'NoneType' object has no attribute 'action'. I commented out the whole for cycle, so now it doesn't delete the keyframe (if exists) and this way works fine. If we can figure out something to fix this then it would work just fine as I wanted! $\endgroup$
    – Alex
    Commented Dec 6, 2015 at 13:55
  • $\begingroup$ Hi @Alex, fixed the bug on line11 (actually noticed it during testing but forgot to amend the code), please have another go at it and let me know if it fixed the problem. $\endgroup$
    – TLousky
    Commented Dec 6, 2015 at 14:02
  • 1
    $\begingroup$ Tested - works for me :) $\endgroup$
    – p2or
    Commented Dec 6, 2015 at 14:03
  • 1
    $\begingroup$ Right. It works absolutely fine, just as i wanted. I really appreciate your help:) Have a wonderful day. $\endgroup$
    – Alex
    Commented Dec 6, 2015 at 14:07

You must log in to answer this question.

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