I’ve been making and sharing animated loops on my Tumblr for a while now, and I’ve received some questions about how I make them.
One of the most common questions involves how to get lots of moving objects to seamlessly loop, even though they’re all entering and exiting at different times and going different speeds. There are lots of tricks and techniques for doing this. Here’s one of the most basic, but still very flexible and powerful.
In all of my animations, the time (often abbreviated t
) runs from 0 to 1. I have a loop that computes each moment of time for which I want a frame, and then I make the frame for that moment.
Let’s suppose we have a scene of 100 circles flying left-to-right. They each have their own start time (a number from 0 to 1) and a duration (let’s say between .5 and .75). So for any value of t, we can find out where the circle is at that moment. Rather than saving a duration, you can also save an end time, which is just the start plus the duration. The drawing above shows the value for the X position of one of these circles. You can see it enters the frame at around t
=.6 and has a duration of about .8.
But wait a second, the graph shows time from 0 to 2! What’s going on? That’s actually the secret.
Because it’s a loop, time (t+1)
is the same as time t
. So the section of the graph that rises from about .6 to 1.4 repeats in the future from 1.6 to 2.4, and so on, and also (most importantly for us right now) in the past as well, from -.4 to .4. That’s the bit of the red wedge at the far left of the picture, near t=0
.
So if every object has a variable startTime
from 0 to 1, and a variable endTime
that is larger than startTime
but less than startTime+1
, we can write a little test to get back the value we want. Here I’m using Processing’s built-in function norm()
, which returns a value from 0 to 1 representing where the first argument is located in the range given by the second two argument.
float blendValue = norm(time, startTime, endTime); if (time < startTime) blendValue = norm(time+1, startTime, endTime); locationX = lerp(startLocation, endLocation, blendValue);