(bell dings)
- Hello, and welcome to a Coding Challenge.
Woof! (gasps)
If you can't tell, I'm really excited about this one,
so I found this GIF the other day by Etienne Jacob,
Etienne Jacob with my American pronunciation,
and look at it.
It might look familiar to you.
It looks like that heart curve
that I made in a Coding Challenge previously.
Now, I can't say for sure whether this GIF was made
based on my heart curve Coding Challenge,
but I can say for sure that the work of Etienne
is amazing, and phenomenal, and uses a special technique,
one that I have recently rediscovered in a dream.
It came to me in a dream, (giggles)
and yet it's been on the Internet at least since 2017,
if not before that,
so what's going on here that's so fabulous and exciting
is this chaotic scene,
this scene of randomness and smoky heart beauty
is actually a perfect GIF loop.
How is it that the end matches the beginning?
And this is something I talked about
in my previous Coding Challenge, GIF loop,
where we looked at, ah-kay, well,
we can move something across the window.
We can rotate something, and as long as the last frame
matches up with the first frame it will loop,
but if it's all random and chaotic, how do you do that,
and so there is a technique for doing this.
Golan Levin actually also has an example bit of code
which describes this in more detail,
which I will show you later in this video,
but mostly what I want to do is highlight to you
this blog post from Necessary Disorder,
Etienne, from November 15th, 2017,
which explains this technique of creating GIFs like this.
Here's another one!
How could this even possibly work?
And so to begin this discussion,
I mean, hopefully we're going to get to lots,
I mean, I could probably make videos on this topic
for the rest of my life, (giggles)
but to begin this topic
I'm going to return to a previous Coding Challenge.
You may remember me from Coding Challenges
such as Coding Challenge number 36, blobby.
It's when I used to wear T-shirts
with this other logo on them during my Coding Challenges,
so what is the issue with blobby,
so I'm actually, even though I could just pull up that code,
I'm going to (fingers snap) speed-code blobby again.
You're going to see, watch,
this is like two Coding Challenges in one,
so I'm going to what I'm going to do is I'm just going to say,
let a equal zero,
a is less than TW0_PI,
and what I'm going to do is
I'm going to use my polar-to-cartesian coordinate
♪ Song ♪
and I'm going to say let x equal
r times cosine of that angle.
Let y equal r times sine of that angle,
and then I'm going to make up some value of r.
r is going to be 100.
I'm going to translate to the center of the window,
and then I am going to say beginShape.
I am going to say endShape, and I'm going to set a vertex,
vertex at x, y, and I'm going to say,
and I spelled TWO_PI with a zero,
which is really complaining.
I mean, I'm just saying, stroke 255, and noFill,
and there we go.
Look, I just wrote the code for drawing a circle
because this is a for loop,
I'm going to go through all these different angles.
I'm going to calculate.
I'm going to increment the angle.
I'm going to keep the radius constant,
and I'm going to set all the vertices x, y of the circle.
Now, if r is random,
and this is basically what I did in blobby,
if I say random 50 to 100, look what we've got.
We've got this kind of like crazy, flickering thing,
and that's almost, that's kind of interesting unto itself,
and I could make this a little more evident
by making the time step,
not the time step, the delta angle, the amount of angle,
the amount of vertices I'm drawing, fewer.
I could make sure it connects at the end by saying CLOSE,
and I could also, just for right now, say noLoop
so we only get one of them,
so each time I run this sketch
I'm going to get a new random pattern.
Now, here's the thing.
This actually looks like it's a nice, closed loop
because it's all random.
It doesn't matter if the last vertex
doesn't match up with the first vertex,
and I, it would be somewhat smart of me
to be more thoughtful about what this delta angle is,
like maybe it should be something
that divides perfectly into TWO_PI,
or the radians equivalent of 360 degrees,
but again, I'm not going to worry about that.
What I'm going to do is
I'm now going to add Perlin noise to this.
Now, if you've never heard of Perlin noise before,
I will refer you to a playlist
where I talk about what Perlin noise is, and how it works,
and a whole bunch of other videos (giggles) related to this,
so go and watch those now if you want,
or I also link to some articles about Perlin noise,
but I'm going to assume knowledge of Perlin noise
for the purpose of this Coding Challenge,
so right now, what I'm going to do is
I'm going to say let t equal zero,
so I'm going to use t as my offset
through the one-dimensional Perlin noise space.
I'm going to change that to x offset in a little bit,
but let's just keep it at t right now.
Then I'm going to say r is noise
of t times 100,
so interestingly enough, let me,
we get a different size circle every time I run the sketch
because we're getting a new,
randomly seeded set of Perlin noise values, (sighs)
and I'm never moving through that time space,
so each vertex is exactly the same,
so what I want to do now is say, in the loop,
t plus equal some amount of delta,
so you now you can see look at this,
ew, the amount is changing, but it doesn't match up,
so first of all, let me clean this up a little bit
by saying map, let me use the map function,
which I think will make things a little simpler.
Perlin noise always has a range between zero and one,
and let's say I want that range
to go between like 100 and 200 now,
so we can see, look at that.
Now, there's just this little every time I run it
you can see that the last Perlin noise value
doesn't match the first one.
This is the problem.
This is getting at this idea of a perfect Perlin noise loop,
and let's also have delta t,
the way I move through the Perlin noise space.
Siri, what should delta t be?
(crickets chirping)
(computer boops)
Let me change that to this
and you could see even if I move t faster,
and I get this kind of smoother blobby-like shape,
it always has this artifact: the last and by the way,
if I took off this close, this will be more evident.
Why, right?
The last vertex doesn't match up with the first one.
This is the problem,
so I have my title of this video right here,
Perlin Noise Loops.
Perlin noise,
if we're talking about one-dimensional Perlin noise,
Perlin noise values in a one-dimensional space,
with this being the t-axis, the time axis,
although really I should call this x offset,
'cause it's like the x offset.
This is maybe a graph of those values,
and if I'm arbitrarily going
from zero to some fixed endpoint
as I get to the end of that circle,
whatever value I get here, which may be, let's say,
if this is between like zero and one,
maybe this is 0.3,
and this is like 0.621, right?
They don't match up.
If I were to take this and twist it around into a circle,
they don't match up.
There is another way
to look at a space of Perlin noise values.
This is a way of looking at it in one dimension.
Let's now look at it in two dimensions,
so instead of having a sort of one-dimensional graph
of Perlin noise values,
I present to you
a grid of Perlin noise values.
The idea is each one of these represents some value
between zero and one.
Now, in this two-dimensional space,
each one of these values represents,
uh, is also a number between zero and one,
so I might have like 0.2 here, 0.3 here, and 0.31 here,
and 0.26 here, and 0.19 here, right?
Every single space has a value.
Now, here, in one dimension,
any number in one-dimensional space
is quite similar to its neighbors,
and it only has two neighbors:
a neighbor on the left and a neighbor on the right,
or a neighbor after and a neighbor before.
Here, each value has how many neighbors?
One, two, three, four, five, six, seven, eight.
It has neighbors above, and neighbors below,
and neighbors to the right, and neighbors diagonally down,
so the idea here is that
what if I could walk around
through a two-dimensional Perlin noise to get space
to get random values and always end up back where I started?
And why not just walk in a circular path?
I mean, that's kind of what I'm programming anyway,
so why not just do that?
What if I were to start here
and get this Perlin noise value, then this one,
then this one, then this one, then this one,
then this one, then this one, then this one, then this one,
then this one, then this one, then this one,
then this one?
I would have one, two, three, four, five, six,
seven, eight, nine, 10, 11, 12,
13 random numbers,
and the last one is going to match up with the first one
if I keep going,
so I could get those random numbers over and over again,
and if the amount of numbers there is so vast,
it's going to appear
like it is a continuous sequence of total randomness,
smoothed with Perlin noise,
so this is the theory,
so let's now go and apply this theory
to this particular blobby shape,
so before I get back to the blobby code,
what I have here is a processing sketch
that is visualizing Perlin noise in a two-dimensional space,
so this a pretty sort of like classic visualization
of 2-D Perlin noise as this cloudy like texture,
and this is literally (giggles) exactly this,
where each number,
each random Perlin noise value in the two-dimensional space,
is represented as a pixel
with a brightness value between zero and 255,
and when you look at that you get this cloud-like pattern
because the color seamlessly, smoothly moves between white,
to black, to gray, to black, to gray, to white,
so the idea here is what is what I want to do
is walk around this space and pull those numeric values,
but map them to something else besides a pixel value,
and walking around in a circle
is a nice way to create a loop,
but you don't necessarily
have to have that be in the circle.
All right, so let's go back to the blobby code.
Now, there's a (giggles) a bit confusing about this,
because in addition
to the walking around the Perlin noise space
in a circular path,
I also happen to be walking around the 2-D canvas
in circular path to draw this circular shape,
so I'm kind of using the same idea,
but with two different points of view,
and hopefully that is something that makes sense to you.
Hmm, okay, so what I want to change this is
I no longer want to have t.
What I want to have is x offset
and a y offset,
and I want to get the Perlin noise at x offset, y offset,
so this is now what I had before if the,
if the x offset and the y offset,
right, basically, if what I'm doing is
I'm asking for what is the Perlin noise value at this spot
it's going to give me some random value,
and then it's going to make that the radius of the circle,
the blob shape, as it goes all the way around,
so I want to start moving,
but I actually don't want to start here.
I want to start over here, so I have to deal with the fact
that I need to find this point
if zero, zero's in the top-left,
'cause I can't have negative values
in the Perlin noise space,
so this is a little bit of a tricky piece of this,
but I want to use that same
polar two-cartesian-coordinate formula,
or I want to say x offset equals cosine of that angle,
y offset equals sine of that angle.
Now, look at this.
I've kind of got something, but it's weirdly symmetrical,
and the reason is this exactly what I just alluded to.
These x offset and y offset values
are going between negative one and one,
and so in fact I'm trying to look at Perlin noise values
over here, right, and these, this doesn't exist.
The Perlin noise space is all positive,
so there's actually a very simple solution to this,
which is just to add one,
and let me add the CLOSE back in,
and let's run this a bunch of times,
and we can see, there we go,
we have this nice random blobby shape
where the end matches the beginning.
Now, I want to do, let's do some more stuff here.
All right, so first of all,
what happened to that like incrementer?
What happened to t plus equal 0.01?
Now I have x offset and y offset.
Why aren't I saying x offset plus equal one,
0.1 y offset plus equal one, one,
well, actually, the incrementer is this.
It is the 0.1 here.
It's cosine of the angle,
and in truth (giggles) I could (gasps)
have a separate variable
from the one that's incrementing in the loop,
'cause I could move through,
but I need to go all the way to TWO_PI,
so that I don't want to do.
I think a way for me to vary this in a more flexible way
is actually to use the map function,
so if I map cosine of a,
which goes between negative one and one,
and then map that to a range between zero and two,
we have, and this is sine,
we have exactly what I had before, and I can keep,
I can keep rewriting the sketch,
and I'm getting a new version of the shape,
but interestingly enough, what happens?
This is really now something that I should make a parameter,
so for example, what if I were to say let,
I'm going to make this a global variable,
let, I'm going to call this noiseMax equal two,
and I'm going to put this here,
and watch what happens if I make this five,
versus 10, versus 120,
versus 0.1,
and look at this, 0.3, 0.5, look at this.
This is really kind of interesting.
With just a little bit of noise,
I have what appears almost like a perfect circle,
but it kind of looks like a human being like me
with faults, I have lots of faults,
drew that circle.
It's got some slight so Perlin noise
can actually be used as a nice way,
with a little subtle Perlin noise,
of making some perfect shape,
like a letterform, or a circle, or a square,
have a little bit of wiggle to it
that makes it seem more hand-drawn,
so that's a nice thing to see.
The other thing we could see here very quickly is like,
it would make sense for me to create a slider.
I'm going to say,
and this I'm using the p5 dom library to do this,
which is a really quick way.
I'm going to create a slider which has a range
between like 0.1 and 10,
and I'm going to start it at,
actually let's make it between zero and 10,
and I'm going to start it at zero,
so I have the slider here now,
and then I want noiseMax to equal,
noiseMax to equal slider.value, so watch this.
Okay, that didn't work. (giggles)
Oh, I have noLoop in here.
No wonder.
Take out the noLoop, and now let me slide it.
Oh, we need to make that,
so first of all I need to give myself smaller time steps
when moving the slider, so let's do this.
Oh, look at that.
Look how it kind of like unfurls,
because it's the same noise space;
I'm just stretching or shrinking it.
That is a really
imagine now if I oscillated that like along a sine wave,
so another thing that I could demonstrate to you
is interesting, like a always starts at zero,
'cause I'm going from zero to TWO_PI,
but if I am walking around the path of a circle,
there's no reason why I couldn't start over here
and then go around, or start over here and then go around,
and why not vary that?
That would be like the phase.
You go back to my Fourier, all of this stuff,
it's so crazily interrelated it's amazing,
but if you go back to my Fourier transform videos,
where we created these orbiting epicycles,
we had this concept of phase,
which is where does that orbit start,
so let me show you another thing here.
Okay, what I'm going to do is show you
so let me make a variable called phase,
and we have that be zero.
I'm going to have cosine of angle plus phase,
sine of angle plus phase,
what do you think's going to happen if I increment phase?
Well, it looks like the shape is rotating.
I mean, it is rotating, or it's not rotating.
I mean, visually it's rotating,
but I'm actually just starting
at a different point in the noise space.
Interestingly enough, like,
and if I make that much smaller, you'll actually see that,
like smaller than the amount of vertices I have,
you start to see a wiggle between them, so interest,
like I could make this 0.2,
and that's going to be even more apparent.
Like, look at that crazy weird wiggle
as those vertices adjust their spot,
and then I want to show you something even crazier, I think,
like let me put it back.
Let me sync it so this is like a nice perfect rotation.
What if I only apply the phase
to the x offset or the y offset, like different phases?
That is really, oh, it's kind of too fast,
so let me do like this.
Like, this is really weird.
You can sort of see
it's almost like there's something crawling around the edge.
(screams in mock horror)
There's so much weirdness stuff going on,
so anyway you see the sort of like exciting possibilities
of thinking about Perlin noise values
as one-dimensional,
values that might pull over time
in a sort of one-dimensional fashion,
but actually pull them from the path of a circle
in the two-dimensional Perlin noise space.
Now, what I happen to be doing in this
is taking a very literal approach. (giggles)
I'm taking those Perlin noise path values
walking around a circle,
and applying them to like the distance
from the center of a circle over,
as I'm drawing it, so it's very like,
it's a very literal sort of like visualization
of Perlin noise values in a circle
to the edge points of a circle,
but if you start to think of other ways
you could apply those values,
the creative possibilities explode,
and so in other words, if this is what I started with,
I have Perlin noise at the beginning.
Sorry, I have Perlin noise values in one-dimensional space,
but they don't loop,
why not take those looping values here,
along the path of a circle,
and then unfurl them
back into the sort of one-dimensional line,
and then use those
to apply to any value in any visualization
to make a perfect GIF loop.
That's the idea, so I'm going to just put that out there.
I'm putting this out into the universe.
I mean, I didn't put it out in the universe.
Lots of other people, Etienne, and Golan,
and many others I'm sure I'm forgetting to reference,
have done this for years, so give that a try.
In the next video I'm going to come back and actually do that,
so I'm going to see if I can take this idea
and just apply it to something.
Probably won't be that visually interesting,
but this will hopefully give you lots of ideas
of what you might make.
Let's add one more thing to this.
I think I can.
This video's not too long so far.
All right, let's add one more thing, so first of all
let me take out this idea of phase for a second.
Just going to comment that out and put it back to what it was,
which is just this,
so every time I run it I get a new Perlin noise space,
and incidentally,
the only reason why I'm getting a new Perlin noise space
is because p5, behind the scenes,
will seed Perlin noise with a random seed each time,
but I could actually control it.
If I were to say noiseSeed,
and pick some arbitrary seed value,
and random number seeds is maybe a topic for another video.
Every time I run this
I'm going to get exactly the same shape each time,
so anyway, just pointing that out,
but there is something else.
There is another way I could animate this,
and it has to do with three-dimensional Perlin noise,
and Perlin noise can exist in any n-dimensional space.
In p5 and in processing,
I think the functions that give it to you
just support one, two, and three dimensions,
but let's talk about
how you might use three dimensions here,
so for example, if this is my visualization of Perlin noise
in two-dimensional space, that cloudy image,
well, I could make sort of like a cloudy set of cubes
in three-dimensional space,
but another way of thinking about it is
have that third dimension be slices of an animation,
so I could show you the Perlin noise space
over time
while incrementing a z-value, a z offset,
and show you Perlin noise for a z offset equals zero,
then z offset equals 0.1, then 0.2, then 0.3,
and so that actually, all of those will have numbers.
Each one of these numbers is now
has all of its neighbors to up, and down,
and left, and right, and forward, and backward,
are similar random values,
so this is actually a really easy thing for me to add.
I just need one more global variable called z offset,
and this needs to be global
'cause I'm not resetting it back every time through draw.
Each time through draw I'm incrementing it,
and I can just add zoff right there
to the Perlin noise function,
and then I can say zoff plus equal some amount, like 0.1,
and you can see we get this wiggle.
I can make it 0.01,
and I'm going to move through the Perlin noise space
much more slowly,
and once again we could see, like,
there's all sorts of things I could do here,
like this could be 0.3, and this could be 0.1.
There's so many parameters that you can explore here.
I think we've made, actually, a super-interesting shape
for like a crazy animated game version of Asteroids,
like you can imagine having Perlin noise
jiggling, oscillating, looping, noisy,
polar Asteroids.
That's a thing you should make,
so anyway, so I hope you find some variations for this,
and I look forward to seeing what you make,
and stay tuned for the next video.
(bell dings)
(playful music)
(bell dings)