(bell chimes)
- Hello, I am here today to make a coding challenge
about making a perfect,
well, whatever I make won't be perfect,
but you might make something perfect, GIF loop.
What is a GIF ?
Why am I talking about GIF loops?
One way that artists,
who work with computational media and computer graphics,
are distributing their work and expressing themselves,
is through posting GIFS
to various different online platforms.
An artist I would like to highlight is Saskia Freeke,
who has been making daily animated artworks
for quite a long time.
In fact, maybe there have been
at least 1500 of them, which is mind-blowing.
You can see a whole variety of them here on her Tumbler.
There's some others, I think she wrote a Medium article
about this process; they're amazing.
A vary famous GIF artist
that I have referenced in previous videos,
in fact we do the whole coding challenge
trying to recreate his Cube Wave,
is Bees & Bombs.
Bees & Bombs creates looping GIFs
through algorithms and math and graphics
in all sorts of astonishing mind-blowing ways.
So I encourage you to check out Bees & Bombs,
also Tumbler, to see all the kinds of GIFs
that he has created.
Golan Levin at Carnegie Mellon University,
a professor at Carnegie Mellon University,
gives an assignment every semester in his course,
interactivity and computation,
to create a GIF gallery.
To create a GIF.
And this is a gallery of GIFs
that students made in his course just this past fall.
Golan has a lecture about
loops throughout the history of art.
It's very easy to think as we work
on program the computer and make algorithms
visual and happen, and JavaScript in the browser
with your OpenGL shaders, WebGL magic,
that this is the first time any of this has happened.
But this kind of work has been happening
for years and years,
and predates the dawn of computers themselves.
This is a wonderful set of historical references
to different kinds of loops
that people have been making.
Of course, here's the iconic Muybridge galloping horse
that you can see here.
I encourage you to check out all these resources,
read about loops, and in fact,
you don't even need to watch this video.
You can go right here to Golan's loop templates,
which has templates for Java,
I'm sorry processing Java, p5.js JavaScript,
and Python processing, as well.
What I'm going to do though in this video,
is I'm going to make my own.
I'm not worrying about making something
artistic or innovative or beautiful;
I'm just going to try to to show the process
from start to finish of writing code and processing,
rendering it out to files,
and then packaging it up as a GIF that you could post.
I'm also going to come back and do a follow-up video.
There's a new p5 JavaScript GIF library
that some students of mine worked on
last semester in a course about open source,
so I will come back and show that
in a future video, as well.
Let's get started.
What is the driving principle of making a loop?
If you look at Golan's code examples,
the core idea is to have,
first, a total number of frames.
So a GIF, an animation, is just rendering
a whole bunch of images in sequence
at some frame rate for your eye to see,
and it appears like animation.
If whatever it is in the last frame,
or really what would be the last frame,
the sort of frame before the last frame,
matches up exactly with the first one,
then you have a loop.
In other words, if I'm drawing a circle here,
and then it goes to here, and then it goes to here.
Oh! (snickers)
Ah, I don't need that many frames!
(eraser scuffles)
Then, it goes to here, and then maybe it disappears.
I have a blank frame.
Then, in theory, the next frame I want to see
would be it entering the screen again,
but instead, I go back and show the first one.
So this will look like it's a loop
that's going on forever, whereas in reality,
it's just starting at the beginning,
and the last frame matches that,
and so as it goes back to the beginning,
it appears infinite.
So this is the idea.
A couple things we need to do,
we need to keep track of what is the total frames we want.
And then, I also want to have a variable called percent.
The idea is that percent
will always be the current frame count,
will be the current frame, the current frame number,
divided by the total number of frames.
What you'll see in Golan's example,
let's do the same thing,
is if we have the ubiquitous processing draw loop,
draws looping over and over,
we could actually put our codes somewhere else.
Maybe I'll write a function called render,
and we will just call that function
render passing in the percent.
This is a function that knows
how to render some kind of design,
based on what percentage of the way
through the design it is,
and as long as the design
is thought of in a way where the end matches the beginning,
then this is going to work out.
All the other stuff about saving frames,
is it recording, is it not recording,
that could be in the draw loop.
Okay, this is way too much explanation
for something that's, in essence,
quite a bit more simple.
All right there's two more things
I want to say about this.
One is Golan writes in his instructions to his student,
sketch on paper first, so I really recommend you do that.
If this is something you want to create,
try working this out with crayons, markers,
charcoal, pencil.
That's really helpful.
Going into that sort of analog drawing process
will really help you think through your ideas.
Number two is what are the kinds of algorithms
that move a value from one number to another number
and in fact, maybe wrap back around?
And there are all sorts of different kinds
of ways of doing this.
Probably, if you've watched any of my videos before,
you might be thinking of the sine function.
The sine function is an excellent function
to use for a GIF loop because if it starts
the sine function, if I were to graph the sine function,
where it starts, it repeats.
It's a repeating pattern over and over again.
You could also use, and then there's a ton
of different kinds of easing functions.
There's this lovely website called easings functions,
easings.net, which is an easings function cheat sheet,
and I highly recommend you explore
some of these other easing functions
to think about how you might move a value
through this loop.
If you go to Golan's code,
I'll just click on the processing one for a second,
you can see even in the code here,
there is a double-exponential easing sigmoid function.
Anyway, that's a wrap;
that's exploration you can do separately from this video.
Let's just get the basic mechanics working.
All right, so I have a blank processing sketch now.
I'm going to go add setup.
I'm going to draw, let's make it a square 400 by 400.
Let's make the background zero.
Ah, but no, no, no, no!
Let's make another function called render,
(typing)
and this function is going to take a value like percent,
and what I'm going to do is I'm going to say render percent.
I'm going to say the percent
is by the, I'm going to make my own variable called counter,
divided by the total frames.
So this means I need to say,
okay well, I want to have 120 total frames in this GIF.
I want to have a counter equal to zero,
and now here, let's just do something like say,
the ellipse, an ellipse is at percent times width,
this would a be linear function now,
at height divided by two, which is 20 by 20.
So we can see now,
if I say a counter plus plus,
and I'm going to say a float counter,
let's just see if this works for a second.
This is the idea that I have a total number of frames.
I have a counter that's going to go up.
I really want to start the counter at zero,
and then count up by one after render.
The idea here is that a much more complex,
sophisticated design with all sorts of
different kinds of easing equations could be in here,
but let's just see if this works.
I have some brackets out of place.
Oh, I have some weird extra slash here.
Let's see what happens, there we go.
So we can see it's moving across.
It's almost like the ball is moving,
because it's being drawn based on a percent.
Now, how would I determine how I want to render this?
I'm just going to have mine start recording
right when the program starts.
If you look at Golan's example,
it's a bit more sophisticated.
You press a key, it starts recording.
Here what I'm going to do is
I'm just going to say
right here, save frame,
GIF dash pound pound pound pound, or hash, png,
and I'm going to make a folder called output.
What this function does is
it saves an image to the directory output
associated with this processing sketch as a png.
Those hash symbols, or pound symbols,
indicate to processing that you want the file name
to be numbered based on the frame count.
(mutters)
I want to use my own counter.
This is going to use the automatic frame count,
which is probably fine,
but what I want to do is I'm going to say,
plus number format counter four, plus png.
So I'm going to add the counter number in here manually.
I think that's going to be better.
This number format function says
use four digits no matter what.
I guess I can just use three.
'Cause I know I'm going to have 120 frames,
I only need three.png.
And then what I could say is if counter equals total frames,
I'm going to just basically say exit here.
I'm going to tell the program to stop.
So let's run this.
Okay, now
Let's look, I have a folder called output.
I look in there and we can see, there we go.
I have all my images.
So there are tons of ways now I could take this,
and stitch it together as a GIF.
I'll show you how to do the stitching later.
In fact, this new p5 library actually
will just create the GIF for you,
but it's kind of nice to have them as separate images.
We do have this question of,
do I have it matching up correctly?
We should see the last one should be 119,
and it's there so my algorithm is perhaps, not the best.
(chuckles)
This percent times width isn't really a perfect loop,
so that's something I might need to improve.
Let's make something that I know
will be a perfect loop.
For example, one thing that I could do now
is I could say, all right let's use rotate.
So I'm going to say angle equals percent times two pi,
(typing)
and I'm going to translate to the center of the window.
I'm going to say rect mode center.
I'm going to say no stroke, I mean no fill,
stroke weight two, and then I'm going to say
rectangle at zero, oh, rotate by some angle.
Rectangle, uh uh, let's use square,
square at zero, zero,
with a side length of 100.
Let's look at this.
Oh, what just happened?
No fill, oh stroke 255.
And this should have a fill of 255.
So now we can see this is spinning.
Now in theory, that's going to match up perfectly,
because that rotation at two pi
equals the rotation at zero,
and I'm rendering the very last frame
right before percent would be one,
in which case it would actually be two pi.
So this should match up perfectly.
Lets now, I'm going to comment this out.
Let's do this one more time.
Let that spin, stop;
now let's actually make it a GIF.
(bell chimes)
All right, so now that I've made all those images
and saved them to the output folder,
I'm going to try stitching them together as a GIF.
(chuckles)
I'm using this website called ezgif.com.
It must be easy 'cause it says so,
so I'm assuming this is going to work great.
But there's lots of other ways.
You could use Photoshop, you could use Ffmpeg,
there's all sorts of different software tools
that you can use to stitch images together into a GIF.
Lets see what happens if I select choose files.
Luckily for me, it's already going straight
to my output directory, but you'd have to find that
through your finder or
whatever operating system you're using.
I'm going to do select all.
I'm going to click open, and then I'm going to say,
upload, and make a GIF!
And this is going to work, right?
Oh look, so we can see.
Oh look at this, we can see all that the delay is 20.
20 milliseconds would be, I guess that's somewhere
between 30 and 60 frames per second.
And somewhere here I should be able to say...
Oh, I can change the delay time.
So 60 frames per second would be 16.7.
Let's just leave it at 20.
Loop count, loop forever.
I'm not going to cross-fade frames.
What's interesting is that it has that effect.
Let's try this make a GIF.
Oh, but I have to wait now.
It's baking in the oven.
(cheerful music)
(alarm buzzes)
Okay, it says right there on the page,
in 1/100 of a second.
1/1000 of a second is a millisecond,
so 32 milliseconds of delay is maybe around 30.
Let's make this three; we want 3/100 of a second.
Let's just try that; let's see what happens.
I mean, you might want it slower,
but I want it to kind of look like
how it looked like in processing,
so let me click this again.
There we go!
Look, there's my GIF, there's my GIF!
Is it perfectly looping?
Sort of hard to tell, but now
I should be able to download it.
I can probably just right click and do save image as,
and say, put it on the desk top.
Yay, I made a GIF, I made a GIF, I made a GIF!
(chuckles)
So you get the idea.
(sighs)
All right, so ultimately the creativity of what you design
is really the thing that I'm hoping
that you will discover in yourself to make something.
I mean, I just made a rotating square.
I think, in order to be able to explore your creativity,
you're going to need the tool you're working with
to be a bit more flexible.
Because right now, the way that I set this up,
is it kind of runs it once and then quits.
But I really want to be able to watch the thing loop,
and then change the code, and run it again.
Change the code, and run it again.
Then when I'm ready, hit record.
Let's do that this way.
I could do that through an interface,
but I'm just going to make a boolean variable called record,
and I'm going to set that equal to,
right now I'm going to set that equal to false.
Okay, so what I'm going to say here is,
I'm going to say float percent equals zero,
and this is very similar to Golan's, if record
then I am going to use this counter that I have.
(typing)
And then I'm also going to do all this stuff
like saving the frame.
Otherwise, I am just going to say percent
equals
float, the actual frame count.
I mean I could always use the counter.
This is silly, I should just always use the counter.
But frame count modulus total frames
divided by total frames.
So this should really be,
this is a little bit goofy,
but I'm going to put the percentage around here,
the parenthesis around there.
Okay, so let me give myself
a little bit more room to look at this code.
This is weird, I got to explain this for a second.
Why, why, why?
There we go, okay.
What I want to do here is I want to have the frame count
always cycle back to zero,
which I use the modulo operator,
and then I want to divide that by total frames.
But I want to convert that into a float,
so I don't need these extra parentheses
or this parentheses.
Yeah, no, yes, this is what I want, ah!
♪ I will refactor this later ♪
♪ You know I will refactor this later ♪
So now that recording is false,
this is just going to play the loop
forever and ever again.
I know that I ever want to now record it, right,
if I like my design, I can go
and delete what was there before.
Which I don't have to, it'll override it,
and I can change recording to true.
Then I can run it, it will do the recording,
and then it will quit out.
You know what?
There's a mistake here.
I am recording the last frame and I don't want to.
Let's look at the output here.
Yeah 120, I don't want 120.
Because 119 is the frame right before the starting frame,
the way that I've coded this,
so this save frame has to be here.
That's a quick fix.
Let me delete that again.
Let me run this.
Here it goes, record.
It's going to draw that last one, but that's fine.
Now, we should see the very last frame.
Shouldn't it exit before it does that?
All right, well, let's do it this way.
Let's just check if it gets to the end.
If it gets to the end, the frame before the last one,
then quit.
Now here we go, (gasps) that works.
I can see just based on where it's stopped.
Okay, great.
So this is what I want,
I want the last frame to match up before the first one.
Okay, great, so there's so much other stuff we could do.
I could use a sign wave.
Again, I would encourage you
to look at different easing functions,
and try all sorts of different kinds of designs,
and your only limitation for yourself
is that the end matches up with the beginning.
Matching up can mean a variety of different things,
'cause something can leave the window,
and then enter again.
As long as there's visual continuity, your GIF will loop.
I will come back; I will show you also
how to do this in p5.js in the browser
with a special new p5 GIF library that I've been testing,
thanks to the creators of that library,
and I will come back and show that to you in a future video.
Please, ah!
Share you GIF loops!
We're going to have them all over the place, I dunno.
Coding train GIF loop, no what should it be?
Just share them.
(mimics crying)
Just look at this video's description.
I will explain how to share them.
Goodbye, thank you.
(upbeat music)