Per Character Text Animation

Is it possible to do per-character animation on text? I’m creating a lot of animated titles and want to create animations similar to this:

Peek 2020-04-02 19-01
Peek 2020-04-02 19-06

I’m aware that I could create a Text node for each character but given that I’m creating a lot of titles it would be much quicker if I could animate the text from the input of a single Text node.

The last animation can be done with a simple expression on the text node. The first one is a bit more tricky (if you don’t want to use multiple text nodes), maybe a Python plug exists(?)

1 Like

Thanks, I’ll look into using an expression for the first example.

I may make a feature request as it would be good if there was a general way to do any sort of animation on a per character basis (e.g. this in After Effects https://www.youtube.com/watch?v=b1_p2qQf_Ts )

I’m attempting to replicate the typewriter text in the last animation using expression and I’m having a bit of trouble. I have two Text nodes. One with the original text and another Text node with the following expression:

originalText = original.text.get()
output = " "

while frame < len(originalText):
	output=output+originalText[frame]
	ret = output

And in the python console I have this

ERROR: while executing script:
ret = app1.new.text.expression0(15,0)
Python error:
Python exception: local variable 'ret' referenced before assignment
Traceback (most recent call last):
	File "<string>", line 1, in <module>
	File "<string>", line 22 in expression0
UnboundLocalError: local variable 'ret' referenced before assignment

I tried it in Python3 (swapped out frame for a counter) and it worked.

Hello, your expression will result in an infinite loop, maybe you are going to make expression like this? (If I understood correctly)

originalText = original.text.get()
output = " "
ptr = 0
for i in range(frame, len(originalText)):
	if frame<len(originalText):
		ptr=frame
	else:
		ptr=len(originalText)
	output+=originalText[ptr]
ret = output

Results in (assuming original.text = “ASDF”):
0-th frame: AAAA.
1-st frame: SSS
2-nd frame: DD
3-rd+ frame: F

1 Like

I’m trying to make a typewriter text effect with each letter in a word appearing one by one. I’m still learning Python and your example really helped! Here’s how I adapted it to make it work

originalText = original.text.get()
output = " "
ptr = 0
slowFac = 4
for i in range(frame/slowFac, len(originalText)+1):
	if frame/slowFac < len(originalText):
		ptr=frame/slowFac
	else:
		ptr=len(originalText)
ret = originalText[0:ptr]

Peek 2020-04-04 20-49

It disappears once the text is typed but it’s a start!

1 Like

You might consider using Retime node (set speed to 1/4) instead of slowFactor, it will make things easier and gives you more control over animation.
Just saying…

I did actually try adding a Retime node after the text node but it doesn’t do anything. In fact the animation disappears once I add a Retime node and change a setting. A possible bug?

I just tried a Retime node on the time code expression from here Displaying a timecode in Natron and it produced some very odd results! ntp attached.

retime_timecode.ntp (15.9 KB)

@hellocatfood, same problem here - odd static results. Maybe, Natron’s Retime node messes the value of the frame variable. However, Reverse input works ok, changing the Speed is not ok.

From: https://natron.readthedocs.io/en/rb-2.3/plugins/net.sf.openfx.Retime.html, Speed property says that:

How much to change the speed of the input clip. To determine which input frame is taken at a given time, the speed is integrated from the beginning of the source frame range to the given time, so that speed can be animated to locally accelerate (speed > 1), decelerate (speed < 1) or reverse (speed < 0) the source clip. Note that this is is not the same as the speed parameter of the Nuke Retime node, which just multiplies the speed value at the current time by the time to obtain the source frame number.

I think the problem is there, it should just multiply to get the value of frame.

Retime works well, and if speed is a constant, integrating a constant from 0 to t is the same thing as multiplying it by t.

This is another bug, please file an issue on github and attach the project file.
These two modifications work (Constant is just black and transparent):
image
image

1 Like

the reason for this issue is that Text has an inifinite TimeDomain. Here it is, compared to Rectangle:
image
image
@rodlie Do you understand why that plugin behaves differently?

1 Like

There is @cgvirus’s TextAnimate pyplug but I don’t know whether it can do these things…

I tried @cgvirus’s plugin a few years ago but it didn’t work. I actually wrote something about trying to find alternatives to it.

Judging by the open bug reports seems like it’s still the case Issues · cgvirus/Natron-Hack-Dev · GitHub

Thanks for the suggestion of using a Constant node, that works.

I’ve added the bug report here Adding a Retime node causes expressions to cease functioning · Issue #476 · NatronGitHub/Natron · GitHub

Thank you for filing the issue!

1 Like

I know this is video is titled links, but this is building a typewrite style expression where one can put any text into a separate text node/field and the expression that links to the field will type it out over time.

1 Like

I did a writeup recently of using Natron to do per character text animation along with a couple of examples of it being used Typewriter Text Revisited Revisited | Antonio Roberts

2 Likes

Hmm, not positive, but I think the reason you might have been having issues with the count pause is it looks like you were using integers, which would restrict one to time units of 1 as the minimum. If a floating point value like 1.1 had been used, then the pause time should, in theory, have been more controllable.