(whistle toots)
- Hello, welcome to a coding challenge, Fourier series.
So what I am going to program in JavaScript
using the p5.js library is exactly this.
This is what's known as a Fourier series.
It is a series of wave patterns that when summed up together
approximate some other function (sighs).
What does that even mean?
So first of all, I'm going to show you some resources,
the things that got me thinking about this topic
and wanting to make this coding challenge.
And you probably should, if you want,
stop this video and go look at these other resources,
and then you could come back, if you wanted,
or maybe you're just off doing something else.
So pretty recently, Smarter Every Day
came out with a video called,
What is a Fourier Series? Explained by drawing circles.
This video reminded me
of a amazing video that I had watched
at some point in the past (laughs),
I guess like almost a year ago, called,
what is the Fourier Transform? A visual introduction.
You probably, if you've done any coding and programming,
you've probably heard these terms before,
FFT, fast Fourier transform.
It's usually referenced in the context of analyzing sound.
And so Smarter Every Day's
video was a collaboration worked with a Turkish
researcher
on this website here.
I'm not going to attempt to pronounce it,
or I will, bilimneguzellan.net.
I encourage you to check out and read this whole article,
but this is a visualization, again,
of exactly what I want to do.
A series
of wave patterns visualized as a path along a circle,
periodic functions summed together
to approximate a square wave.
And if we can make this happen in JavaScript,
then there,
in theory, is no reason why we couldn't then figure out
how to draw any given path
as a series of Fourier transforms.
And there's a sort of a well-known GIF or video
of this like crazy set of circles drawing Homer Simpson.
So I'm hoping to get there.
But this video, I just want to, by the end of this video,
have exactly this pattern in JavaScript.
Okay.
So I also want to
reference this website, betterexplained.com,
which has a nice article, An Interactive Guide
To The Fourier Transform.
And
this, I think, is a really excellent explanation.
So again (laughs), going to get started coding in a second,
but you're just going to have to humor me
to let me kind of talk about this a little bit more
just to get my feet under me here.
So the idea of a Fourier transform, so a sound wave, right?
We have this idea of a sine wave.
You've probably seen me draw sine waves
on the board or in code in countless videos,
usually drawn something like this.
A sine wave has a frequency, (laughs)
which is how often does it repeat.
Like if this is the sort of x-axis,
like this is one whole cycle, right?
And I could think of that as like the time it takes
for this dot to go all the way around the circle.
So frequency is like
how many cycles of the wave per unit of time,
like per second or per frame.
There's also amplitude.
Amplitude is the height (laughs) of the sine wave,
how much distance between the very top and the very bottom.
And so a sound can be represented as a sine wave.
So but you probably have seen like,
oh, I've got this recording device soundy thingy
and what it's doing is it's like (vocalizing),
this is like the sound.
This is the wave, this is the representation
of the sound that I'm listening to right now.
Well, you can create, this kind of wave pattern
is typically actually the sum of multiple wave patterns.
So in other words, if this is like the musical note a,
and then this is like the musical note something else,
and we're to add these two waves together,
can you still see what I'm drawing?
You know, I might get something that looks like this, right?
And I've done this, I think I had like
an additive wave video in the Nature of Code series
about this kind of idea.
The idea of the Fourier transform is can we go,
right, I could have these two waves.
I could add them together and get this pattern.
Could I go in reverse?
If I'm listening to a sound like this,
could I pull out all of the waves,
the sounds, the frequencies, that make up that?
That's like pitch detection.
Or if I wanted to then filter out
a very high-pitched sound, if I could take the sound,
break it apart, take away one of the high-pitched sound,
add it back together, I would get this.
So this is akin to, I love this metaphor here
in the Better Explained, of unsmoothie-ing a smoothie.
So I'm (laughs), I like to make smoothies, actually.
Little-known fact.
I always like, anyway, so but could you like, right?
Let's say I take some mango, and some kale,
and some blueberries, and some like almond milk,
and I mix them all together, and I give it to my children,
and I say, I made you this beautiful smoothie.
Can you guess what's inside of it?
This is actually a game we actually play at home,
I've just realized, like, ooh, this is perfect.
Well, if I could do a Fourier transform,
I could take the mixed smoothie and filter out,
go in reverse, and find out all the ingredients.
That's the idea.
So that's the idea of the Fourier transform.
That's conceptually what it is.
Now what I want to do in this video is I'm not going to worry
about figuring any of this out (laughs).
I'm just, now I understand what it is,
what it can be used for.
I have this goal eventually of having it,
of using a Fourier series to draw any arbitrary path.
But one way to get started with that
is exactly what's demonstrated here on this website,
which is, what waves do you need to add up together
to end up with a square wave?
And you can see here,
this is actually a really nice visualization,
is as you have more and more iterations
of the Fourier series, how it converges even closer
and closer to the square wave.
I could also just go here to Wikipedia and find this again.
So this is the clue.
So there's this idea
in a Fourier series of Fourier coefficients,
and some kind of like iterative thing,
of like n, and n plus one, and n plus two.
And we can actually see a nice clue to that in here.
This is actually a very,
this is one of the simplest Fourier series.
What is the series?
One, three, five, seven.
Can you guess the next number?
(bell dings)
Nine, right?
And what's after that?
11.
So if I can just implement this,
each one of these circles and have them rotate around
like this at that period or frequency,
with that amplitude, we're going to get somewhere.
So let's (laughs), I've talked about this for way too long.
Let's try to actually code this now.
So the first thing that I want to do,
I'm just going to start, I'm going to start like kind of even
not thinking about the Fourier series.
And I'm just going to make up a variable called angle.
You could really think of that as time.
It might be more appropriate for me to call this time,
'cause time is moving forward,
that's a sort of crucial idea.
And I'm just going to say, time equals,
every time through draw, if you haven't worked with
p5 before, draw is a function
that loops over and over again, over and over again.
So time is moving forward.
Then what I want to also do is I just want to like
draw a circle somewhere (laughs).
So I'm going to translate to like
200 pixels over and 200 pixels down.
I'm going to have this idea of a radius,
like the radius of a circle that I want to draw
is maybe going to be 50 pixels.
And then I'm going to say ellipse at zero zero,
with that radius times two, because the ellipse function
expects a diameter, radius is half that.
I'm going to make this white, so I'm going to say a stroke 250,
oops (laughs), stroke 255.
Somebody told me how to get rid off that auto-complete,
and I still haven't done it, and a noFill.
So when I go back the to browser and refresh it,
I've got a nice circle there.
Eh, let's make it a little bigger.
All right.
So now what I want to do is, how can I have that dot
traveling around the circle?
Let me have the dot traveling around the circle.
So the way that I would do that is I would use
a polar to Cartesian coordinate transformation.
And I'll certainly have a video
that talks about how to do that.
But what I'm talking about here is if this is the radius,
and this is the angle, which is really in my program
the time, how far over in x, and how far over in y,
how far up in y, can be calculated based on trigonometry?
So the radius times cosine of the angle,
or angle is the x value,
the radius times the sine of the angle is the y value.
So I'm going to do that here (laughs).
I'm going to say, let x equal radius times cosine (snorts),
cosine of time.
And let y equal radius times sine of (laughs),
sine of time, I should probably pause and turn that off.
And then, but let me get through this first,
point x (laughs), I'm definitely going to pause
and turn this off, point x, y,
I'm going to say stroke 255,
stroke, actually, let's make this a circle also.
So we're going to say like ellipse x, y,
we're just going to make it smaller, like eight pixels.
And let's also say fill 2225.
My god, this is making me crazy.
And here we go.
Look at that.
There's that circle moving, right?
That circle is moving, and maybe it makes sense to also
draw a line (laughs) from zero, zero to x, y.
And now I've got this, and I want it
to move a little bit faster, and honestly I'd like it
to go the other direction (laughs).
I'm not sure, I should actually check.
What is it doing, if I want to like recreate
exactly what's here, yeah, yeah,
it's moving the other direction.
So I've got the beginnings of this.
Now I haven't worried about the number four here
and the fact that I've got the angle divided by pi,
but we'll get there.
(bell dings)
I'm back, I fixed this auto-complete thing
that was bothering me in Visual Studio Code.
I'll put something in the description
about how I did that (laughs).
But I also wanted to mention, in the chat,
thank you to Amar who mentioned,
who wrote, you are using Fourier series
and Fourier transform interchangeably,
but they are not the same thing.
So thank you so much for that comment.
I will post some links, also, in the video's description
for more reading about this.
But in short, the Fourier series is for periodic signals,
which is what I'm doing exactly right now
to create this square wave, which is periodic,
versus the Fourier transform which is for aperiodic signals.
Another way of thinking about that is to represent
any general non-periodic function.
So hopefully as I get further down this road
I will come back to this.
And also in the chat, Smarter Every Day writes,
Fourier transform is for swapping between frequency domain
and time domain, which is an also really nice way
of putting it.
Okay.
But what I am doing in this video is implementing
the Fourier series, the periodic series
that add up together to the square wave pattern, okay.
So here we go.
So I have success step one.
I have my circle passing around.
And you know what,
I think that I probably should not put this,
I'm going to have this not go the correct direction (laughs).
I'm going to have it go, 'cause I don't know
if the negative number's going to mess things up there.
Okay.
All right, next step.
Next thing that I need to do,
and if we go back to this particular video is,
how can I take this circle that's moving around
and then draw the resulting wave pattern?
And this is actually kind of a much simpler problem
than you might think, because basically what I want to do,
this is moving in two dimensions.
X is oscillating back and forth,
y is oscillating back and forth,
and both those oscillating at the same frequency,
with the same amplitude, in the same phase,
the starting together, is moving along
the path of the circle.
So I just need to take the y value
and sort of graph that along the x-axis.
So the way that I'm going to do that
is I'm going to add an array to my code.
I'll call it wave.
And it's going to be an empty array.
It has nothing in it.
And what I'm going to do here
is every time I calculate a new y value,
I'm going to say wave push y.
So I'm just going to save that particular y value.
So now all I need to do is say,
oh, you know, for let x, you know, I'm not going to use x,
i equals zero, you know, i is less than wave.length,
i++.
Let's draw a point
at i,wave
index i,
right?
So all I'm doing here is saying, let's just draw
all of those height values,
those y values, from kind of x equals zero
to as many as them as I get.
So if I do this, you're going to see, look at that.
There's that wave pattern going.
All of those points are moving up and down,
and you could see how the height of that wave
is the same as the thing passing around.
Okay.
But this doesn't look so great, so what I want to do is,
I want to instead use beginShape and endShape, this will,
and then say vertex.
So this will actually like tie it all together.
And I think if I say, no, it's actually,
I'm going to leave the fill
in there for a second, 'cause it's kind of cool.
So that's actually like trying to fill in the shape,
which is sort of a nice pattern.
But I'm going to just say noFill.
I'm going to do this, refresh the page,
and then also, you know, why not translate,
just to translate this a little bit over, like 200 pixels.
And we can see this.
And then I guess I could also draw, sorry,
I want to draw a line.
Yes, I want to draw, sorry, I got confused what I was doing.
I want the end of the line to connect to where that is.
Oh, of course.
Okay.
So what I want to do is draw a line,
so I need that first, I need this first value.
So I have x minus 200,y
to i,wave
index zero.
And actually it's not i, it's zero.
So this is just, this I know is the very first point
in this loop, and since I translated it over,
I've got to like back up with my x value.
This is very awkward.
♪ I will refactor this later, you know ♪
♪ I will refactor this later ♪
Oh, no, no, no.
I don't want it to connect to here.
♪ You know I will ♪
What am I doing wrong?
♪ This later ♪
Why is what I'm doing different?
Oh, this is, oh, I'm doing it backwards.
Ah, I know what I'm doing.
Ah, I'm doing it backwards.
I actually don't want to, I'm appending
to the end of the array, so every new value I'm appending
to the end, I want to add it to the beginning,
so instead of using push, there we go.
Instead of using push,
I want to use unshift.
So unshift is a JavaScript function,
it's a weird name for it, but push just adds this thing
to the end of the array,
and I want to add it to the beginning.
Well that was a mistake (laughs).
There we go.
This is what I'm looking for.
There we go, okay.
(bell dings)
So now I have the line (laughs) connected there.
Now the one thing that's a little bit of an issue here
is that I'm just like adding points, and adding points,
and adding points, and adding points,
and never getting rid of any points.
So I also should probably,
at some point I should say something like, if wave.length
is greater than like,
I'm just going to make up some number right now.
I could do a calculation of how many points I need
'til the end of the canvas,
but just to move more quickly here I'm going to say wave.pop.
So that would just like, if its getting more than 500
it's going to like delete the last one off the end.
And now we should be able to say like,
should be able to look at wave.length.
It's 152.
So clearly I only need like,
I don't need more than 250 (laughs).
All right,
we are at this spot.
Now comes the exciting part.
I mean this was hopefully all somewhat exciting,
but to me, how do I suddenly go from,
I have one circle
with a point around it,
to another circle that's kind of like there
with another point around it,
to another circle that's like there,
with another point around it?
How am I going to add up these circles
and continue this path of x y along them
with all of these points rotating and spinning?
And guess what?
I've actually done this before.
I'm just remembering now.
I have a video on the fractal spirograph.
Let's see if this comes up.
Yeah, the fractal spirograph.
I actually did this already in the fractal spirograph
which was this way of doing exactly this
to create a fractal pattern.
I should go back and revisit this.
But so we'll see how this compares to that.
So let me go back to the code here.
And so what I want to do, so first of all,
before I do this, I think I need to think
a bit more formally about these functions.
So before, I was just saying radius, cosine of time.
Radius, sine of time.
But these functions actually map
to
these values on Wikipedia here.
So
if theta is my time, basically,
what I'm saying is that I want to, and
the value multiplied by that
is four divided by, sorry (laughs), theta is my time,
and four divided by pi
is basically my radius, in a way here.
Hmm, kind of conflating some things, but I can start to use
this particular series to understand
the ratio of the radii and the frequencies
between these different oscillating circles.
Okay.
So let's say, what if I, what I do is I say,
four divided by pi,
times cosine
of, what was it again, one times time.
So I'm going to do this.
I'm going to just change the y
here.
And then what I'm going to do is I'm also
then going to say, radius times that.
And also I missed something.
Four divided by what?
One times, this is confusing, 'cause there's a one here.
One times angle, one times pi.
So I'm going to say, that's one times time,
one times time, and this is one times pi,
and this is one times pi.
So the number that I need to change
for each circle is that one.
So let's just see what this actually yields.
And I'm going to go back to here.
t's sort of, it's the same thing,
but now the fact that that radius is 100,
it's like everything is way too big.
This is going to get calculated here.
♪ I will refactor this later ♪
Let's put this in here.
And then let's make this back,
just 'cause I don't really need,
let's make this back to here.
Okay.
And four time, divide, oh, oh, this should be one times pi.
Aah, why is it doing that to me?
I don't want that, I want these divided by each other.
Visual Studio Code is too smart for my own good.
There we go.
That's what I wanted.
That's why it was giving me something crazy.
Anyway, okay.
Radius is not defined.
Now I need to draw the circle next.
This is when things are going awry.
And let me put this here.
There we go.
And now
there we go (laughs).
Of course now it's the, of course,
I had the division in the wrong place.
I have this tiny little wave (laughs).
So let's go back and make that about
25, which I think would be now basically,
oh, it's divided by pi, aah!
This is going remarkably well (laughs).
Here we go, okay.
I'm happy with where I am now (sighs).
Okay, so now I have that first circle.
The reason why I am doing this,
the reason why I'm putting this,
making all this work, 'cause I want this to be n.
I actually want this to be a number that changes, right?
First,
first it's one.
Then it's three, then it's five, then it's seven.
That's not too hard to figure out, right?
And what I want is to start x with, and y, both at zero,
and just add this stuff together.
So x+=,
y plus equal.
So I want you, and then what I want to do is I want to loop n.
So n is going to go from one, I'm going to say zero, actually,
n, 'cause I'm, you got to count
starting with zero, n is less than two, n++.
And then, and this really by the way, I should call this i,
because what I'm really saying is, what n is.
How do I do one, three?
And this is pretty easy.
I multiply it by two and add one.
N equals
i times two, plus one, right?
When i is zero, I get one.
When i is one, I get three.
When i is two, I get five.
So now if we add these together,
and I'm just doing two right now.
And speaking of which, I should move this
all the way down to here.
And I'm going to take this out for a second.
And then I've got to go back here, and there we go.
Okay, so something is wrong.
I kind of got things close.
Like look at this interesting,
weird, crazy pattern that I've got.
Okay, so the line should not be going from,
so I should be actually keeping track of
previous x is x,
and
previous y is y.
So I don't, I'm going to like take out the wave for a second,
'cause I'm going to have to change what I'm doing here.
And then I'm going to just say, previous x equals x.
Oh, no.
And then
previous x equals x, previous y equals y,
And then the line is going from those.
Like that's not always going from zero, zero,
it's going from the previous x, y.
So now that's right.
The other circle also needs
to have its
center be at previous x, previous y.
And there we go.
So now I have this.
I have this circle spinning around this circle,
spinning around this circle (laughs).
There's only two circles.
But, in theory, if I change this to like five,
there we go.
Now we're gettin' somewhere.
And actually, it would be sort of fun,
I really think that I should think
about how I'm drawing this a little bit better.
So, for example, I think I want these circles
to be much lighter, so let me give them a little alpha,
and then the points should also be,
I think the points are almost like less relevant.
I almost don't want to draw these points.
Let's just, 'cause I have the lines now,
so let's see what happens if I take this out.
And I do this.
Oops.
But I want the line
to be
full brightness.
There we go.
So this is what I'm looking to draw.
So there we go.
So I now have this particular series with every circle
next to,
rotating around every other circle.
And
I am using the values from the particular Fourier series
for a square wave.
Okay (sighs).
So now, and I feel like this should be bigger,
it's so small.
Let's go back to
having this be 100, and let's actually not,
let's just see how this looks.
And let's, we can also,
I'm being like neurotically silly about,
let's move it over a little bit. Okay.
Oh, I don't want it to go off the screen though,
so let's leave it there.
Split the difference, 75.
Okay.
And then (laughs), I'm going to have to go back a little bit.
This is very silly, what I'm doing.
Okay, there.
Aah (laughs)!
I have a problem.
150.
There we go.
Okay, so I know this is hard for you to see.
Let me zoom in on it, so this is what we got.
So now guess what?
We're basically done, right?
Before, I just had to put the wave back.
Let's see what happens if I just put that wave back.
Like what happens?
I put it back.
Now what's going on?
Like, I'm getting some crazy thing here.
I don't even know what's going on.
Oh, I'm pushing all the values.
I just want to push the last value.
Actually, this is going to be a much easier fix, I think.
I just want to push the last value after the loop is over.
And there, we can see there we go.
But I don't know why this isn't shifted over.
Did I lose that somewhere?
I need to do this.
This I actually going to be, oh, this was actually fine.
And there we go (laughs).
(bell dings)
We are done.
I should add one more thing to this
because this is going to become relevant later
in my mind's eye of how I'm going to do more videos on this.
So right now I am visualizing the resulting wave pattern.
Also, I got to do a few more things.
Will you please bear with me?
You know, this video's already about 70,000 hours long.
But let's at least add one slider.
I'm going to use the, and so I'm going to have a slider
that gives me values between one and 10.
And I'm going to start it at one.
And what I'm going to do is I'm going to take a slider.value.
So basically I'm going to
have n control the amount of iterations
of the Fourier series itself.
So now you could see there's just one
so I can use this slider to add two, three,
four, five, six, and this is 10.
And I could, this is fun
that I can actually do this in real time.
So now you, the viewers of this video (laughs),
will have so many more creative and interesting ideas
about how to make this prettier,
how to create more interactivity, there're so many,
but this is just an inkling, right?
Every system that you build with code
has a bunch of variables and parameters.
There's no reason why you couldn't make those interactive.
You couldn't even, we could make the number of iterations
match to a sine wave itself, like (mimics explosion),
what would happen there?
That would be crazy.
But now you could see up to 10.
We can see how it's converging.
And I, you know, I don't know how far I want to push this.
Let's push it a little.
Let's try.
(drums rolling)
Let's put it at 100.
That's zero, all the way up to 100.
Performance-wise, it's fine.
It's happy to do 100.
There we go, look at this.
Look at that square wave!
Oh, that is like (laughs)
the nicest square wave I've ever seen.
This is making me so happy.
I cannot explain to you.
I feel like this is a thing that's been in my head
for years, but I never actually like sat down to program it.
'Course I am standing right now, but close enough.
Thank you for watching this coding challenge visualization
of the Fourier series for making a square wave.
Check this video's description for a link
to the Coding Train web page with this challenger
and the code for it, and then if you make your own variation
of this, please share it with me.
You could contribute it right there.
So what are some things you might do?
Number one is, why don't you try doing this sawtooth wave?
Could you take the code that I wrote and make it do this?
That would be a nice exercise to try.
What other kinds of interactivity can you add to this
in terms of changing the colors,
changing the different parameters, changing the view of it?
What happens if you do this in 3-D?
Like, right?
Could you have some kind of points oscillating
over three dimensions in a sphere,
and then map that to something?
That would be cool.
So I look forward to seeing what kind of things you create.
Ask your questions and all of that nonsense.
But it's not nonsense, it is, I mean, whatever (laughs).
Goodbye!
(whistle toots)
See you next time.
(sprightly music)