Seamless animation
As discussed here I wanted to talk about seamless animation or loops. In preperation I listed some information regarding this from different places which can function a bit as a knowledge base on the subject:
First of all the Cartan Node library 3.6
Which contains the following animation nodes
- Apply_ramp: Use a curve as a ramp to change values during an animation
- Easing: Applies selected easing function to produce changing values over time
- Exponential: change values over time at an exponential rate
- Fade: adjusts alpha (0 to 100) of shapes or geometries
- Rearrange: Move a list of points or shapes to a list of target positions over time
- Reshape: Gradually reshape one polyline path into another
- Settle: Calculate values for a decaying sine wave
- Turtle_drive: Move a pointer around a polyline path
- Unwind: Unwind a polyline path into a line or segmented curve
And some other things:
- The tutorial for basic animation from the documentation
- The tutorial for the animation of subnetworks
- An explanation of basic animation
- An example of an animation loop
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by john on 03 Apr, 2025 11:10 PM
Floris,
Thanks for kicking this off. I hope we can make this a useful collection of posts about animation. I will add a few to the thread to help get us started.
First, a quick note about the animation nodes in my library...
I originally called this category "Change" instead of "Animation" because the same techniques used for animation can often be more broadly used to code the more abstract concept of change.
One example is gradation. The same easing nodes you use to control the speed of animation can also be used to control the shifting patterns of color in a gradation. Another example is controlling the rises and falls of a curve. Gradation is change across space; animation is change across time.
Some of the nodes in that category could arguably be placed in other categories instead. Fade, for example, can be used in contexts that have nothing to do with animation. But all of the nodes in this section have a "change" port that you can plug a frame node into. Here is the change port for each node:
For students of animation, a fun exercise would be too hook each of these into a frame node and use it to create a different animation.
Support Staff 2 Posted by john on 04 Apr, 2025 01:18 AM
ON SEAMLESS LOOPS
The first thing to observe about the art of making seamless loops is that there are situations where you DON'T want to make your animations loop seamlessly. Sometimes you want to emphasize the end of each cycle, not conceal it.
One example I did long ago was an animation about gradual change called Evolution. It shows an ape-like silhouette turning into a man-like silhouette so gradually that you are barely aware of any change - until the image snaps back to ape at the end of each cycle. That snap was the whole point:
https://www.instagram.com/p/COHMoZQney8/
But normally you do want to return to the beginning at the end of each cycle. Seamless loops are the temporal equivalent of seamless tiles.
One of the easiest and most pleasing ways of doing this is by using the wave node to create the gentle back and forth movement of a sine wave. Sine waves, being based on circles, always return to their starting point at the end of each period.
To use this technique all you have to do is hook your fame node to the Offset port of a wave node (set to type Sine), and set the period of an even multiple of the length of your movie.
For example, If your movie is 100 frames long, you can set your period to 100 and the output will fall, rise, fall again, and rise back to its starting point in exactly 100 frames. For more vigorous motion you can use any other even multiple of 100 for your period: like 50, 25, 20, or 10. I find that a period of 60 makes a soothing motion; if I use that I will make sure my movie runs for some multiple of 60.
There are countless other techniques for generating values that change and then return to their starting values. You can take any closed shape, feed your frame node into the T port of a point_on_path node, and then lookup the x or y value of the point traversing the shape's perimeter. Those x and y numbers will return to their starting values after 100 frames and will convert the inflection points of the shape into speed changes of the movement. For movies that are some multiple of 100 frames just feed the frame into a mod 100 node. For movies of any other length, just use a convert_range node to convert the length of the movie into a 0 to 100 T value.
Perlin noise produces charming and mysterious patterns that are ideal for animations. My noise node is great for making clouds that drift across a sky; just feed your frame node into the z port of my noise node.
But one drawback of Perlin noise is that there is no way (I know of) to evolve a pattern back to an earlier state - for the same reason that clouds in the sky never drift back into exactly the same pattern they had an hour before.
Even here, though, there are ways to create seamless loops if you try hard enough. One technique (which I haven't yet tried but which should work in theory) is to make a large landscape using Perlin noise, tile that pattern by reflecting it, then move a "camera" across the pattern until you cross the tile border and return to your starting point. (By "camera" I mean using a translate node to move a pattern across the window of the canvas.)
If this was done in a straight line you would see an obvious "rewind" effect as the clouds would move across the sky and then travel backwards. But with some effort and cleverness you could conceal this to some degree by taking a circuitous path across your landscape while also gently zooming in and out (using the scale port of the noise node) with a wave node.
A simpler way of looping Perlin noise is just to feed a sine wave into its Z port. If you render the noise directly you would see the clouds in the sky ebb and flow with the sine wave. But if you add in other effects you can conceal this effect. I used this technique on another animation long ago called "Movement in the Deep":
https://www.instagram.com/p/B_tldNhn3r6/
The noise is driven by half a sine wave that gradually falls from 600 to 0 and then rises back to 600. If you look carefully you see a flowing motion half way through the video that moves down and then reverses and moves back. But because of the way the noise was rendered into shifting angled bricks that are colored independently, the effect is not at all obvious.
There is much more to say on this topic. If anyone here has questions about how to make a particular animation seamless, I will be happy to help.
Support Staff 3 Posted by john on 04 Apr, 2025 01:43 AM
STOP ACTION ANIMATIONS
I've made many posts about animation over the years. I will share one here because it might help you think about animation in a different way:
http://support.nodebox.net/discussions/show-your-work/130-forest-an...
The idea is draw each frame of your movie onto a giant poster and then use the translate node to jump from frame to frame. It's dirt simple; you just have to make sure each frame is the same size as your canvas.
I've never actually used this technique in one of my art pieces. It's not really necessary to draw out each frame ahead of time. But the underlying concept could produce some interesting effects if you used it to move your "camera" across the face of an image. Instead of a boring back and forth path, you could use my Hilbert node to create a space-filling Hilbert path. Or just overlay an ellipse and step through the intersecting frames. A normal photograph would probably produce seizure-inducing flickers, but a photo of sky or sea or leaves or fur or something with gentle gradations might be intriguing.
This technique might also be useful in cases where each frame is time-consuming to create. You could let your computer run overnight to create all the frames, save the poster as an SVG, then just import and move the camera to quickly edit and create the movie.
Just another idea to play with!
4 Posted by florisdejonge on 04 Apr, 2025 05:45 PM
John,
Thanks for the replies.
I was triggered by what you wrote in to looping with Perlin noise.
I used the method with putting the frame-node onto the offset port-of a wave-node. I also used a waveform-node onto a ramp-node and connected that onto the z-port of a noise-node (see attached examples, don’t think I’ve posted these before). In this I tried to hide the reversal by changing the colours. But I am wondering whether there are other solutions.
In addition, what I find hard to track is to fine-tune the setting for a specific speed for an animation to loop within a certain duration of the animation.
For example: the wave-form has its settings, the apply ramp has its own, and the convert node the same. Let’s say I want an animation of 250 frames, I feel like I have to align all those settings. Or am I mistaken?
Exporting an animation costs time, so one can’t work that intuitively. Sometimes it takes hours before the result is visible and It turns out that for example the last frame is missing, because I made some error in starting at 0 or 1 with counting.
Any advice is appreciated.
Floris
Support Staff 5 Posted by john on 05 Apr, 2025 12:20 AM
Floris,
I like both of your videos. There is a noticeable back and forth swaying motion, but I think that works well in both cases, and the pacing seems just right.
You do need to align all your settings to the length of the movie (if you want it to loop seamlessly) - no getting around that. But they don't necessarily have to be aligned with each other. That is, each one needs to be a factor of the movie length, but they can be different factors. In fact, making them different can help disguise the reversal.
For 250 frames, you can have one factor repeating every 50 frames, another repeating every 125, and a third repeating every 250. Using lengths like, say, 240 or 360 gives you even more possibilities.
You also don't have to always use waves. You could use some function of the x and y values as you traverse the perimeter of shape, for example. Depending on the shape, this can create more complex movements that nevertheless return to their starting point. I sometimes use a character textpath for this purpose. This also lends itself to multiple factors: you can go once around the shape every 250 frames, or twice, or n times (or just use a different shape for each factor).
For Perlin noise you have two factors to play with: the Z value and the scale. The Z moves things back and forth while the scale moves things in and out. Depending on how you use the noise value to drive different elements, this can produce complex effects. And if the scale changes are based on a different factor than the Z changes, the reversal can become much less obvious.
When things get complex it becomes hard to get the pacing right. With experience you can get good at picking your fudge factors, but ultimately I think there is no substitute for making multiple renders until you get the overall pacing just where you want it.
As you said, this can become painful with long render times. One trick I often use is to use rough cuts to adjust my pacing. For pacing you can go lower res, maybe only use outlines of elements with no gradients, simpler versions of the elements, etc. This can reduce render times by an order of magnitude. Once you are satisfied with the overall pacing and ensure the seams match perfectly you can then crank up all the added fanciness for the final render.
One small trick I also use for seam checks is to flip back and forth between the final frame and zero frame (assuming I am rendering 1 to n frames). If the seams are right you should see no change between these two frames.
Hope that helps!
John
6 Posted by florisdejonge on 08 Apr, 2025 02:54 PM
Hi John,
In regard to our discussion of seamless animation loops using noise values I asked the AI system Claude to come up with a python-script to be used in Nodebox 3 using the information provided here.
After some trial and error, failed attempts and different iterations this resulted in something that might be useful. In the provided file you can find the simplex noise script and an example with a custom node called simplex_noise. Claude also generated some information for its use and application:
the Simplex Noise Node takes the following input:
So you can connect a grid node's x and y values to the noise node's x and y input
To create an animation with the z parameter:
The noise pattern will now animate smoothly as the frame advances, completing a full loop when z increases by 1.0. So this loop is seamless, albeit somewhat fast.
Support Staff 7 Posted by john on 09 Apr, 2025 03:59 AM
Floris,
This is fascinating.
I spent a little time learning more about noise. A very deep topic. There are many types of noise generators and toolkits with many options and settings. I had thought simplex was patented, but I see that patent expired a few years ago.
Your simplex node does indeed create seamless loops every time z increase by 1, but it's so jittery and seizure-inducing that I wouldn't want to use it for animations (without more intervening code). Each frame is wildly different from the next even when you zoom out.
When each frame differs so wildly the value of being seamless is reduced. It is genuinely seamless, but it's such a mad boil that even non-seamless loops are not that much different.
So I spent a little time looking at the algorithm seeing if there was some variable I could change to make it less frenetic. After some research I finally found that the only thing you can do is increase the Z value more gradually. This was the first thing I tried, but I didn't do it enough. Dividing by 1000 instead of 100 makes no noticeable difference.
But, it turns out, dividing by 40,000 DOES make a difference. You have to do something in that range to achieve a more soothing animation.
Attached is a zip file with my slightly modified version of your network. I didn't touch the Python module. All I did was:
Attached are 3 short (100 frame) videos:
I find the transisitions at 40,000 much more pleasing. But there are two obvious drawbacks:
This seems to be a fundamental tradeoff. You might be able to make the Perlin noise animation seamless if you just sped it up by a factor of 400. But then it would be at a mad boil where seamlessness doesn't even matter very much anymore.
That said, I do think this node is interesting and could well be quite useful. I could imagine having a whole new wing of my library with different noise nodes. Before going too far down that rabbit hole, though, I would need to be convinced that the added variety and power is not so complex and confusing that no one would ever touch the new nodes.
I still think you could make Perlin noise based animations seamless with some of the other techniques I discussed above. It depends on exactly what overall effect you are creating and how long your movie is (100 frames may be too short a loop to achieve seamlessness). I might try to make a project to explore this further.
It would also be fun to explore other uses for noise, like world (island / continent) generation.
Thoughts?
Support Staff 8 Posted by john on 09 Apr, 2025 04:12 AM
Oh, one more unrelated thing I wanted to add to this general thread on animation...
I strongly recommend that whenever you save an animation project, you add a comment to your frame node with the recommended movie length. Keep it simple, just "1 to 100" or "0 to 250" or whatever.
I have learned from painful experience that when you come back to an old project, or try out someone else's project, you need to supply the From and To values in the movie dialog in order to export any movies. But unless these are written down somewhere, they can be VERY hard to figure out from the code.
Adding a brief comment to the frame node is a simple fix. I now do this for every animation project I make.
Support Staff 9 Posted by john on 09 Apr, 2025 07:06 AM
Floris,
I did a little more research and have a favor to ask...
Could you ask Claude to generate another version of your Simplex Python Node, this time with 4D noise? It should be just like your current version, but based on a call that takes X, Y, Z, and W (in addition to scale and seed).
4D noise allows you to create 2D patterns and then advance through them not just linearly using the Z dimension, but in a circle (or any other 2D path), using both the Z and W dimensions. This is the key to making noise animations that loop seamlessly.
The simplex algorithm was designed to support 4D noise, and most implementations include that option. But every Python port I found had dependencies including Python 3 (instead of 2.7 which Nodebox runs on) and external libraries like NUMPY. What is so impressive about your Claude-generated module is that it runs on plain vanilla 2.7 Python with no dependencies.
If you can produce a node that takes X, Y, Z, and W, I can wrap it in additional Nodebox code to make seamless animation more intuitive.
Thanks in advance for anything you can come up with!
John
10 Posted by florisdejonge on 11 Apr, 2025 04:00 PM
Hi John,
Thank you for your replies and your suggestions. Some take-aways from that I want to note for a potential follow-up: note the length of animation in a comment and maybe use a (wave) visualization to track the (value) synchronisation between different moving design elements. The going between between first and desirable last frame of a loop I've already applied a lot. Especially in this last experiment in regard to the attempt for a '4d noise field'.
I provided Claude with the earlier Python script as an example. And a txt.file with the documentation on custom nodes.
It took about 15 (!) iterations. Attached is the best result. Just some things I found while using this workflow:
What follows is a selection of the final conversation I had with information regarding the prompts, the results and the use of this node:
I am wondering whether there are more scripts (from Nodebox Live for example) that can be ported to Nodebox 3 in this way. That would open up a whole lot of possibilities (a good packing script for example would be nice).
Support Staff 11 Posted by john on 12 Apr, 2025 03:26 AM
Floris,
Bravo!
Thanks for sticking with this through 15 iterations. In addition to creating a powerful new node, you have also demonstrated a proof of concept that could lead to many more new nodes.
I have taken your original node and made some modifications. I then wrapped it inside a subnetwork to create an even simpler node for general use.
Details:
I discovered one problem with your node: unlike the previous version, there is no seed input. Even worse, the noise was not fixed, but would instead fluctuate every time you relaunched or hit Reload. This means that you could perfect an animation and then find it totally changed each time you played with it.
So my first goal was to add a seed port like your previous version had. I was tempted to ask you to ask Claude, but decided to give you a rest and see if I could modify the Python code myself.
I was finally able to do this, but it was painful. My Python skills have atrophied and Claude's code was a tad tricky. When I tried passing the seed directly the way the previous version did, it worked but ran 20 TIMES SLOWER. Yikes. But with another hour of head-banging I finally found a way to get both seed and speed.
To solve this problem I had to use a global variable for the seed (so I could still use the class instance without having to pass anything), and force the code to always read in the X and Y values as lists. This also required a few changes on the Nodebox side but the end result works just the same and is just as fast as your version.
I made the following changes to the Python code:
I also made some changes to the Nodebox node:
Finally, I wrapped the simplex4D node inside a subnetwork to make the ports a tad more user-friendly:
I also cleaned up the demo a little:
The result is a slightly beefier animation, 3600 pixels instead of 900. In addition to the screenshot and demo, I have attached a sample move:
NOTE: Unlike the Perlin noise node that everyone has been using till now, noise_circle returns values between 0 and 1, not -1 and 1. Depending on how you use the noise values in your project, you may need to make this adjustment if you replace noise with noise_circle.
A lot to absorb, I know, but I wanted to get this all documented before I forgot what I did.
Floris, please let me know what you think of these changes and if you think noise_circle is ready to add to the library.
Everyone else, please take this node for a spin and try using to create some generative art of your own. Although it is designed for animations, you can use it for anything requiring random noise. If you don't want the noise to loop just set the frames per cycle to a higher value that your total number of frames.
Kick the tires, try changing all the parameters, and report back with any questions or suggestions.
If this looks solid I will make a separate post with the official documentation. Then maybe we can talk about what else to do with this raw AI power.
Thanks again, Floris!
12 Posted by florisdejonge on 12 Apr, 2025 07:05 AM
Hi John,
Thanks for the reply and the improvements. I think it looks solid. I did a test run and seems to work well. So I think this could be another addition to the library. (You misspelled your name in the comments in the script by the way.)
Floris
Support Staff 13 Posted by john on 12 Apr, 2025 07:57 PM
Floris,
Thanks. Thanks also for spotting that type in the Python comments. Fixed.
I share your ambivalence about hiding the separate X and Y ports behind a point port. But I think we both agree it's a good trade-off: one less port and no need to separate out the X and Y by hand - which is what would happen 95% of the time. And if you do need to access them separately, you can just use a make_point node to turn that one port back into two.
And you can also access the sinplex4D node directly if you want. That's one of the nice things about wrapping a direct code module node inside a Nodebox subnetwork: you get the best of both worlds.
Actually I would have slightly preferred exposing just the Z and W ports as pure numerical inputs and building the circle myself in Nodebox. That would have allowed loops in shapes other than circles. But the way you and Claude made this, treating W as cycle length and automatically calculating the correct radius for the given speed, is much simpler for end users and what the vast majority of people would want to do anyway.
Finding the right balance between simplicity and flexibility is one of the most important and enjoyable parts of node design.
Speaking of loops, on further thought I think I would like to rename this node as noise_loop instead of noise_circle. That's a better fit with looping animations and seamless loops.
I plan to try making some art with this thing before publishing it, just to make sure it works well in the trenches. I encourage everyone else to do the same.
Support Staff 14 Posted by john on 13 Apr, 2025 10:27 AM
I just posted an animation created by the noise_loop node on Instagram:
https://www.instagram.com/reel/DIYgm96RXkD/
So far I am quite pleased with this node. I can see many possibilities!
15 Posted by florisdejonge on 13 Apr, 2025 06:26 PM
It works fine as a replacement for the earlier noise node (with visible reversal), without editing much else. As as test I reworked an older animation
Support Staff 16 Posted by john on 14 Apr, 2025 12:07 AM
I just published the official version of noise_loop here:
http://support.nodebox.net/discussions/show-your-work/916-noise-loo...
I also started a conversation about Nodebox and AI here:
http://support.nodebox.net/discussions/general-discussion/15334-nod...
I suggest we move further AI explorations to that thread.
17 Posted by abdu on 16 Apr, 2025 12:20 AM
Floris, John (and maybe Claude),
I just wanted to drop you guys a line and say, that I enjoyed seeing that simplex noise loop node come to life and honestly, thank you so much!
I tried to give it a little bit of spin but it got out of hand and became this type specimen poster that I enjoyed the hours I put in, thanks again to you both.
(ps. there is a touch of film grain after the png export, other than that it's all your magic.)
A
Support Staff 18 Posted by john on 16 Apr, 2025 10:02 AM
Abdu,
You are most welcome.
That poster you made is fascinating. If you feel like it, I would enjoy hearing you say a little more about how you used noise_loop to make it.
It seems like every morph from one character to the next follows the same rhythmic motion of jerking and twisting and distorting (even though the actual shapes are different each cycle). So I'm guessing the "shape" of that motion came from noise_loop somehow. Is that close to the truth?
I always enjoy seeing your creations. I'm so glad that noise_loop can be part of it.
J