Get SuperHi Unlimited, our brand new membership service, at a 40% off early-bird price!

How to Make a 3d Waving Flag Using Three.js, WebGL and Javascript

Published

June 17, 2020

Difficulty

Intermediate

Posted by

Rik Lomas

In this tutorial, we add a moving 3d flag, as seen on Yuta Takahashi's website, using the Javascript library, Three.js, to our website.

View demo

Transcript

(upbeat music)- [Rik] One of my students asked how we makethis flag effect on Yuta Takahashi's website.So how do you go about making something like this?Well, I've just quickly made my own versionthat looks something like this.So how do we go adding this moveable 3D flagonto a page like this?So what we're gonna be using is a library called three.js.Now three.js is used in lots of different kind of sites,you can actually look through all of them here.I've just quickly downloaded this,is quite a big pound load.We'll talk about how to do this in a second,but I've also just quickly mademy own version of the site.So quickly just added some HTML.Here we go in the top corner, in the header,we've got a navigation up here,and we've got a .So my flag is gonna go in here.Technically, I could just add an imageand be done with it, but I want something that moves.I've also added the three.min.js file in here.So this is from the downloads.I've also got my own flag.js.So flag.js at the moment, it's completely empty.We're gonna add our code in here.So how do we go about doing this?How do we work with three?Luckily, as part of three, it gives us some documentationthat we can actually use to create what's called a scene.Now here it says before we start,before you can use three,you need to display it somewhere,we've got an HTML file, we've added three.js,we've just done it in here.Now three.js is quite a big file.If you're using the SuperHi editor,what I'd recommend in settings is settingthis to true just to kind of speed things up.It's pretty big file,we might as well kind of optimize things.So how do you actually make a scene?Well, here, we have three.js,and our code goes below it.Since what we've done in our HTML,we've got our three.js and our flag.js,all of our code is gonna go in flag.js.So how does three do things?Well, what we can do is actually just grab someof this code, will alter it as we go.So the first thing we'll dois just take all this code from here,we actually wanna make a scenewith camera and then render it.So I'm gonna copy this codeand I'm gonna put it in to my flag.js.There we go, just pasting it in.Now next, there's some more info,it gives us a bit of more information on here.If you do you wanna read this,I would definitely recommend this,further down is this, we got some geometryand material and a cube and we're movingthe camera position back.I'm gonna copy this one and paste it and under here.Now, the moment we don't have anything in here,but if we scroll down the page,you'll notice this black area starts to appear.If I scroll down, now, eventually,we want this area to be in the middle of the page,not below it.We'll talk about that in a second.But if we look again, back on creating the scene,I scroll down it says rendering the scene.So I'm gonna take this code and I'm gonna copy it.I'm gonna put it in underneath.There we go.Now if I scroll down again,what we should see is this cube appear.Now this cube comes from this geometry,this color comes from this color over here,we add them together, we add it to the sceneand this camera position is five away.Now, if I scroll down even further on this page,you'll see that you can animate this cube as well.So here, I'm gonna take this codeand I'm not going to paste it underneath,it says it needs to be in this animate function.So what do we wanna do?I wanna put it in this animate function,I'm gonna add some space and paste it in hereand there we have a cube that spins around.So this is the basic thing for creating the scene.I've copied and pasted anything,I haven't just done things.But what I want to do is have this on the main partof the page, not underneath.At the moment, what is happening is,this has been added to the body of the page.So this is essentially going, well,after this after this and after this.So currently, our flag is pretty much here on this line,but we wanna put it in this section tag instead.So I need to update my code.So I'm gonna pick this section tag,and add my cube to the section tag.So my flag.js, the first thing I need to do,is pick that thing that I just said.So here, I'm gonna pick this sectionwith a class of flag.If I was in CSS, I'll do it down here as section.flag.In my flag.js, I'm gonna add some spaceand I'm gonna say, well, right at the top,I'm gonna click, pick a constant called section,because it's whatever I want to call itand here in my document,I'm gonna do something to this document,I'm gonna do a querySelector,basically means something like CSSand round brackets to run this and in quotes,what I'm gonna say is section.flag.So here, I've just picked this section.I'm not doing anything with it just yet,but what I can say is instead of this being appendedor added to the body of the page,I'm gonna get rid of this document.bodyand replace it with section.So now my section if I scroll up and down the page,you see my links up hereand technically there's some text up here as well,it's in the main part of the page.Now by default in three, as you can tellthe background is blackbut what we wanna do is make this see through.Now each one of these thingswhere it says new THREE.scene,new THREE.PerspectiveCamera, WebGLRenderer,has a page in the documents.Now I'm gonna look at the WebGLRenderer.So in here, what we can pass in as some extra things,now what I want to pass in here is an alpha channel,basically make it see through.By default, it's false, therefore we see a black background.So in my code, I'm gonna add some extra infointo my WebGLRenderer.To do that, I'm gonna put my cursorin the round brackets and add some curly bracketsand I'm gonna space these out justto make this easier to read.So press Enter and what do I want to put in here?Well, some extra info is all about,well in here, they call it alpha.We don't want alpha to be false, we want it to be true.So I'm just going to say alpha is trueand now we can see this has this background disappeared.Now I can also add in some anti aliasing,is a little bit pixelated around the edge of this cubeand in here, there's another thing I can add in,you can look through these options and say,antialias is false by default,I do want it to have this kind of crispy edges.Here, I'm gonna do a comma after true,add a new line and say antialias is true.This basically smooths these edges of the linesand have this cube now spinning around the page.So I've set up my main part of the page.Only at the moment, I have this cube in green,which is not what I want.So where is this cube of green happening?So it's happening down here.So I've got a geometry which is a box and this boxI've got a mesh basic material, which is colored green.Let's just change that.Well at the moment this class is color of Ox00ff00.

This is basically like a hex value exceptrather than the hash at the start, this is Ox.This is just part of WebGL.So I'm just actually gonna add some tidying up here,I'm gonna space these thingsout just to make it easier to read.There we go, add some space in my geometry,my material and my cube.So if I wanna change the color of this from greenand something else like red,I would just say, well, what is a red color,while I could use like #ff0000and all I do to change that is not just #Ox.Instead, I put ff0000.

So that is now in red.Now this is just a solid color,we'll add some texture to this a little bit later.Now the first thing we want to do,is not make this into a box,we want to make it into a flag.Now, a flag doesn't have a name.It's actually just a kind of flat panel.But if we go back to our WebGLRenderer,it's part of the documentation.If I scroll down the sidebar,I'll get lots of thingsthat I can add in my geometry section,I've got a box geometry, which looks like a box.I don't want it to be a box,what do I want it to look like?Well is it a circle?It's not quite a circle, but it could be.Is it a cone?Nope, I could just click througha lot of these things and see what what comes up.So there's a few things in here that we could use,the one that I'm actually gonna useis called a PlaneGeometry.So there we can see, this is kind of like a flat panel.So instead of it being a BoxGeometry,instead, I'm gonna change this to be not a BoxGeometry,but a PlaneGeometry.So now we have this panel is spinning round in thin air,which is fine, but we might wanna give it a size.So if I go to the PlaneGeometry page,you'll see further down that we can passin some more information as part of this width,heights, how many segments in the width,how many segments in the height.So let's give it a width.The width I'm gonna give it is well,I want it to be flag shaped,so maybe let's make it 5 across, there we go,and how high comma 3.So this kind of feels like a flag shape.Kind of looks pretty good.Now what we want to do as part of the flagis actually move bits within the flag itself up and down.So make it kind of wave.Now at the moment, I can't really tellwhere my points are within the flag.However, what I can do on my mesh basic material,is after the color, I can add a comma and say,well, let's press Enter, wireframe is true.So here we see this in red,but we can actually see all the pointsof this instead now even thoughit's spinning around in space,so by default, a PlaneGeometrywill just have one size each way.Now, what I can see in here is if I go back upand I open this little panel,is if I start to increase these numbers,there's gonna be lots of different pointswithin my plane that can move up and down.At the moment there isn't any plane,any points within this plane,there's no dots in here that can move things up and down.So on my PlaneGeometry,what I'm gonna do is I'm gonna increasethe number of points on this and to do that,I'm gonna do comma, and then say something like 15.There we go, can seethere's 15 going across now, comma 9.So 15 is just five times three,three times three is nine, so this looks square.So what I can do within this nowis move things up and downand move things out of my plane itself.Now at the moment, this plane is moving up and downand spinning around because this cube rotation,I'm just gonna get rid of this for nowand this is just gonna make it flat.So maybe I wanna add a little bit of rotationto this to start with and then move it in and out.So at the moment, it feels a little bit too flat.I also don't want to call it a cube.It's not a cube anymore.Maybe I'll call this flag and then we'll addthis flag to the page instead,same thing, just changing the name.Let's just add a rotation onto this flag.So on the flag, let's gonna add some rotation.rotationand we're gonna set this rotation to be, well, what.There's three ways I can change this rotation, 0, 0, 0.So x direction, y direction and outwards to the z direction.

Now if I changed the last one,so point, change it to 1 for instance,this rotates it around in this direction.Now we don't want it to rotate in that direction,we want it to be flat,maybe I want this bottom edge to bea little bit closer than this top edge.So let's think about it.Let's put some string onto this direction onto this endand we wanna twist that string.So this string I want this bottom endto go around in the x direction.So I wanna rotate this slightly in the x direction, x, y, z.Let's just add point 1, let's see.Now this is going the wrong way.The top is not rotating towards meso it needs to be a negative, there we go.So this kind of has this pattern going out.What I want to do is each of these pointswithin the flag where all of these kind of cross,I kind of wanna move them around.At the moment, they're not moving around,of course, they're just flat,I'm just setting the rotationand we wanna move these points within the flag itself.Now, PlaneGeometry is part of a general geometry classand as part of that, it has things called vertices,basically points within it that we can actually change.So how do you wanna change all these points?Well, basically a PlaneGeometryis just a quick way of writing a lot of this stuff out.What we're gonna do instead is we're gonnapick all these vertices or points within this flag,here, here, here, here, here, here,quite a lot of them and move them in and out.How do I want to move them out?Well, I kind of wanna move them in this kind of pan,this sine wave pan,so move them in and out but closerto the user in the z direction closer.So what we're gonna say is,well, how do I animate all these points?Well, I'm gonna animate stuff and basically say,well, on this flag that I've got this mesh,that is a dot geometry.

Now, the moment the geometry is a PlaneGeometry,which has been made up here,on this geometry, I can get all of the points, the verticesand then what I want to do is update them.So to update them, I'm gonna map them dot map.

I'm gonna run dot map and say for every single point,it's gonna do something.So here, I'm going to say v, goes and does some code,v points do some code.Now here, what I can say is things like,well, I want my movement to happen here.So which direction do I want all of these points to move in?Well, I want them to move them upand down closer to the user.So I'm gonna change the vertices.xand instead, I'm gonna make this equal to something.Now, the moment I could just say they're all one.Let's move this closer to the user.As you can see here, as I'm increasingthat it moves it vertical to the camera,zeros where it normally is,if I make it minus it goes the other way.Now I want them allto do something different based on what.Well on my final one, what I have it based on,is the x direction, we can see this x,if I go across this way,it changes how close it is to the user.So instead of this being a number,instead what I'm gonna make it basedon is something like math.sinand in round brackets, I'm gonna be makingit based on v.x.So here what we can see is this movement now,so this side and this side are very different.They're changing the direction of thingsthat are based on the x direction.Now what I can say is this is always between 1 and -1.So for instance, I could say this is too closeand this is too far away.So across all of this, I'm just gonna say,well, maybe there's a bit of a limit to this,maybe it should be point 5 times this,it's a little bit flatter or make it even flatter,and flatter and flatter back to zero,it gets really flat, permits really, really bigtwo and a half times, gets really close to the user,set like point 5.Now within this as well I can say is this is the kindof size of the wave is point 5.But what can also change in hereis the amplitude how many waves there are.So I can also say this v.x times something like 2.

You can see now there's two waves.I can increase this number of wavesand you can see how all this box starts to align.Now the more waves that you have the more segments you need.So for instance, at the moment are 15 and 9,maybe I want this to be something like 50 and 30.So you can see here now this is a little bit more defined.So maybe 6 is a little bit too much,maybe 2 seems to work,I've got that kind of flag moving aroundand I have this kind of change.Now, of course, what I want to dois change this based on timeand at the moment, I don't have anythingin here based on time,however, the three has something calleda clock that we can actually use.So what I need to do is make a new clockand then basically listen to how longthe clock has been running for.So before any of this stuff starts,all this animate, I'm gonna say, a constant clock.This is gonna be equal to a new THREE allin capitals dot clock and then we're gonna run that.So by default, the clock is not doing anything.However, in my animates, I'm gonna add some spaceand say for each time this animates,I wanna get a progress of the clock.So here I'm gonna say a constant called,let's call it t for timeand this is gonna be equal to the clocks.I'll just clock.getsElapseTime().

Now we're not using t just yetbut we wanna put this in our code somewhere.So for instance, I could say,well, this x is the thing that movesso I wanna add t to this.Now at the moment, it doesn't do anythingand there is a reason for this.We update the geometry, it will just freezeit in time until we say,"hey, we need to update this".So as part of geometry itself,we need to scroll down and in here,somewhere deep in here, we need to update all the vertices.So we set it to true.So what do I need to set it on?The geometry?So here underneath this, on the flag itself,I'm gonna say the geometry needs.What's it called?Let's have a look,verticesNeedUpdate, so I spell that wrong.Let's try that verticesNeedUpdate equals trueand now we have something that moves.So what I'm gonna do next is actuallyadd layers on top of this.So the moment, this is just one single wave.Instead, what I'm gonna say for each of these thingsis I'm gonna space this out a little bitand say, let's give it a constant called waveX1.

So wave in the x direction,let's just have one of them for now,unless it's equal to all of this stuffthat we had from below, then take this,cut it out, paste it in and replace it with waveX1.

So this is doing the same thing,I just moved it to a constant for each individual point,however, we can do now is add a second wave into this.So constant waveX2 and say,well, maybe let's do kind of the same as what we had here.Just change them the numbers.So here instead 0.25 times Math.sin,and we'll do v.x times 1 instead of 2 plus t.

Now we wanna add these two things togetherto make two waves that run on topof each other to make it more natural.So here, I'm gonna add waveX2.

So this is actually quite a more natural wave,and I can change some of this,maybe change that to six, you see this kind of like,weird double wave, and then the kind of miniature waveon here, make this sound like 3,makes it look a little bit smoother.So I can play around with some of these numbersand I can also play around the time here, t times 2.

So this miniature wave,smaller wave half as small as this one,runs on three in terms of the amplitudeand it moves twice as quick.So I have this more natural looking wave.I can also do this in the y direction as well.At the moment I've done it on v.x.However, I could just say as well,I've got a constant waveY1and instead this kind of looks the same.So math.sin v.y direction nowand we'll add t to it as welland we need to add this to the x, the z direction.

So notice now that this moves in this directionand in this direction,so adding them all together makesthis kind of fluid movement.Now, of course, what I could do is saythis water actually is way too much,maybe let's make it point 1.So this kind of looks a bit smoother across,maybe I wanna kind of times this amplitude,it's kind of how many waves they are, they're 5.So see there got three kind of wavesgoing this direction now,maybe we wanna slow down the time so point 5,so it doesn't move that quicklybut I have something that looksa little bit more natural now.Now at the moment, of course,this is just the kind of mesh,it's of the wireframe, wireframe is true.How do I get how do I get some images on this?Well, by adding this stuff in here,makes it really easy to now just putan image on top of this.So what can you use as part three?Something called the TextureLoader.So how do we use this?Well, before any of this stuff starts any of this geometry,what I can see in here is a constant called loaderand this is a new THREE in capitalsdot TextureLoader and then we run it.

Now the loader isn't pulling anything in,but I've already got a few thingsin here that I can use.Something like a book.jpg or a flag.png.I've added these kind of stripesin just so we can see if it's moving,rather than it be too flat.So let's try some of this stuff out now.Now, instead of this wireframe being true,let's get rid of it.

Now we have this red thing that kind of moves as a blob.Kind of hard to see what's going on.So let's add some texture.So here instead of it being texture,I'm gonna say the map of this is basedon the loader which is this thing up here,dot load and then round brackets, and in quotes,the name of the thing that we want to pull in.So for instance, book.jpg is our name.Currently, you might not see this color is in redand we've got some black areas on this,I can actually just remove this color nowand we have this moving and I could just changethis into something like flag.png,for instance flag.png to get something that looks like that.

Now one thing you might notice on my final designis this edge doesn't move, whereas this one does.Try to make it look more natural as a flag.Now the way to do this is I kind of wannahave a multiplier that starts at zero,so this has no movement and this edge has one.So zero to one.Now this edge is currently minus 0.25,this is 5 across and half of that is minus 2.5.So what I can see in here in my waves is also say,let's add in a multiplier,the multi and this is going to be equal to well,I want this edge to not move.So I want the v.x minus, well it's not minus,it's plus 2.5 because this is minus,so the middle point minus the minus,and then let's divide all of this by the width,something like five is the widthand now I'm gonna take all of this,put some round brackets on it,and times it by this multiplier.

So this edge always stays the same,this edge always moves and this is true of the y directionand the x direction as well.So by adding this stuff in really quickly,we can make this flag effects really welland I can actually increase some of these numbers nowand play around with that maybe it's a lot bigger,maybe this is a lot faster as a wave,so it's a bit bigger like that.Obviously, I can try and play with some of the numbersand feel it out but by adding a few bits of three.jsin here, we can actually piece together somethingthat looks really professional and really impressivein just under 50 lines of code.(bright upbeat music)

So one thing you might notice is if I resize this,it doesn't actually fix anythingand there's a few ways that we're actually gonna fix this.So the first thing you need to do is actually makethe camera and the renderer fit to the section itself.At the moment is doing it based on the whole windowand we're just kind of lucky at the moment.At the moment that we have in here,hi, hundred percent VH.If I just change this, for instance,to something like 50 Vh, it doesn't changeso if I just quickly put a border on here,border of red let's say,you'll see that my flag isn't insidethe red area escapes.So let's just quickly fix this.So in my flag.js, instead of this being window widthand window height, I can't use innerwidth.Instead, this is gonna be the sections.clientwidthand the section.client,if I spell it correctly, clientheight.

So this is gonna fix the camera.So we're in the perspective camera.I also wanna fix the render size as well.So same thing here, I'm just gonna changethis to not in the width and the windowis the section.clientwidth and the section.clientheight,there we go.Now this you still have if we changethis is still in effect.How do we do this?So at the bottom of my page,what I'm gonna do is add a little bit of extra code.So down here, I'm gonna say if our window,which is the whole of this area,we wanna listen out and add an event listener to resize.If we resize the window, we want to run some code.So what code?Well comma function round brackets, curly brackets,whenever we resize this page, we want things to change.What do you want to change?Well, there's two things that we updated above.It was the renderer, which changed the sizeand the camera as well.

Now this is part three.js, this is somethingthat is generally used on pretty much every project duties.So we're gonna get this camera aspect ratioand basically say this is equal to what was here.So I'm gonna take this code,which is the section clientwidthdivided by the clientheight.What I also need to do on the camerais say this has been updated.So similar to what we have here with vertices need update,I will need to update the camera by sayingcamera.updateProjectionMatrix and then run this.

Kind of long one, but it still works.So again, you can see this camera changing size,you see that thing getting longer,a little bit there as well,but we need to also update the renderer too.So this one pretty similar to what we have up hereto render a set size,which is just gonna take this codeon line 10 and put it in here.So now if I resize the window,you might notice this stays in the middleand looks pretty good no matterwhat size of the page I'm up.Again, I'm just gonna quickly get ridof this so it's still full width.Good to get rid of my borderand make this 100Vh, still in the middle of the page,and this fits nicely.(upbeat music)

If you're interested in learningmore about WebGL and three.js,take our experimental Javascript coursewhere we have three projects that cover this in detail.(upbeat music)

Want more? Sign up for our newsletter for more articles, resources, and fresh inspiration