Pixel Effects with JavaScript and HTML Canvas - Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today i want to show you something you might not have seen before in this course we will use html5 canvas element and plain vanilla javascript to create interactive animated particle effects we will start with a very simple project where we learn about the draw image and get image data built in canvas methods and how to use them to create a pixel array then we take it to the next level and use that pixel array data to make our particles do amazing things let me show you the real power of html canvas using just basic fundamental javascript tools and techniques if you get stuck or you need some help with the code we are about to write you can message me on twitter or catch me somewhere on my youtube channel special thanks to bo from freecodecamp for letting me share this course with you i hope you get some value out of this let me take you through the code step by step and explain how to use html canvas to create beautiful things [Music] in index.html i create html canvas element with an id of canvas 1. i link style css and script.js files in style css i target the body element and give it black background canvas will have border solid white top 50 left 50 position absolute width 800 pixels and height 450 pixels transform translate minus 50 to minus 50 will center our absolutely positioned canvas element both vertically and horizontally in script js the usual basic canvas setup constant variable i call canvas is equal to document.getelementbyid and i pass it id i gave it in index html which was canvas one constant ctx shortcut for context equals canvas variable from line 1 dot get context 2d this way i make my ctx variable an instance of canvas 2d api object and i can call all built-in canvas methods on this variable now such as ctx fill rectangle to draw a rectangle and so on canvas width will be 800 and canvas height will be 450. these values need to be the same as values we set for canvas in css file to make sure we get the correct scaling i will bring an image with dimensions of 800 times 450 pixels to my project first i create a custom variable i call for example image 1 and i set it equal to new image new is a special keyword in javascript and image with capital i is a special built-in class constructor doing this will just create a new blank object and assign it values and properties based on built-in javascript image blueprint simply said we just created blank new image object that comes with src property if you consulate image 1 you will see it i set this src source property to path to my own image like this image 1 src equals image1png for you the path might be different remember that the path must be relative to wherever you placed your script.js file inside your project structure for javascript to be able to see your image now i will try to draw the image by using built-in draw image method if i just give it three arguments like this the first one is the image i want to draw so my image one variable from line six the second argument is the x coordinate and the third one is vertical y coordinate where i want to draw it on canvas i want the image to start from coordinates 0 0 which is the top left corner the reason the canvas is still blank and no image has been drawn is because javascript code runs very fast even though the image loads relatively fast as well it is not yet loaded when draw image method runs i have to take variable image 1 from line 6 and since i gave it src attribute on line 7 i can give it event listener for load event image 1 add event listener and when a load event occurs which means after the image has been fully loaded only then run the following code i placed inside the callback function if i put the draw image method inside this callback function the image will be drawn so this was the first issue we face when handling images on canvas solved draw image built-in method has three versions depending on how many arguments we pass to it we use the shortest one here with only three arguments we can also pass it five arguments where the fourth and the fifth one stand for width and height of the image my image is exactly the same size as my canvas so i don't need it but if your image is a different size you can give it different width and height doing that you can scale your image however you want by passing it different values here the third version of draw image method takes nine arguments and we can use it to crop out pieces of a sprite sheet for example like we did in my game development series to create animated characters we will not do that today i want to know colors of each individual pixel that makes up this image html canvas provides us with a built-in method that can do just that i create a constant variable called scannedimage and i set it equal to ctx.getimagedata this method takes four arguments which define what area of canvas i want to scan for pixel information i want to scan the entire canvas so i pass it 0 0 for starting coordinates and canvas width and canvas height for the end coordinates this method scans all pixels on canvas it goes from left to right row by row from top to bottom the order is important if you want to determine coordinates for each pixel it scans all pixels and it returns image data object if i console lock my scanned image variable we can see that object here let's open it and we can see it contains something called data which is an array we also have height and width properties which correspond with the size of an area i scanned with the get image data method and it also has some prototype methods which are not important now if i open data we can see it's a massive array but it's not a regular javascript array i can see it is called something like uint8 clamped array and it contains one million four hundred and forty thousand elements uint eight clamped array is a special type of array it is very simple basically each of its elements has to be a number from a certain range it can only contain numbers between 0 and 255 if you used css before you remember rgb color declaration and you will know that any color can be declared by combining three numbers between 0 and 255 and that's what this is we are basically looking at color values uh numbers that make up colors of each individual pixel but it is a bit strange because each element in the array contains only one number and we actually need four numbers red green and blue and alpha for opacity the way this array is structured is that each pixel is spread into four elements so the first four elements in the array combined are rgba color value for the first pixel the next four values are rgba for the second pixel and so on i can see that this array has one million four hundred and forty thousand elements now that we know that four elements make one pixel i also know that my image is made out of one million four hundred and forty thousand divided by four which is three hundred and sixty thousand pixels it makes complete sense because my image is eight hundred times four hundred and fifty pixels in width and height and 800 times 450 is also 360 thousand these pixels are organized in rows from left to right starting at the top left corner so the first pixel is 1 70 139 1 is right 70 is green 139 is blue so that's rgb and alpha of this pixel is 247 alpha or opacity in this array is also declared in a range between 0 which is invisible to 255 which is fully visible that's one thing that's different from the standard rgba color declaration we use in css where alpha is a number between 0 and 1. i was able to scan my image with no issues but in some browsers due to cross-origin resource sharing security you will get an error that says something like canvas has been tainted by cross-origin content and we cannot scan it for image data i don't really know if people can actually put viruses in images or something chrome for example considers your locally stored images as cross-origin unless you run this code through a server if you ever experience this problem when scanning image data with a get image data method for your creative coding projects instead of trying to navigate through server settings and so on there is an easy work around you can actually convert any image into data string and place that string directly into your file this will avoid all cross-origin resource sharing issues we can convert an image into data string using two data url built in javascript method just by calling it on the image but today i will just do it using this quick free online tool if you google png to base64 this will also work on jpeg images and other types by the way not just pngs i go to this address onlinepngtools.com convert png to base64 you can just drag and drop your image here it doesn't have to be in png format on the right hand side it will generate base64 string that contains all data we need to draw this image with javascript without needing the actual image file if your string doesn't start with data colon image like mine does make sure you tick this checkbox down here that says create a valid data image url so i just copy this huge long string in script js on line 7 instead of using the image file as a source if you ever get tainted canvas error in console just replace the path to the image with this long data string and it will fix everything it will give me exactly the same image like this you can see in the console that it contains identical image data and everything it is the same image so as i said this data array i see in console when i console log my scan image variable contains all pixel that make up my image the image was scanned from top left corner going left to right row by row and every set of four elements in this array contains rgba values for one pixel this is all i need to manipulate colors in my image i can save that pixel array in a separate variable if i want let's call it scanned data and it's equal to scanned image from line 12 dot data which is the uint8 cloud array we see in the console i can for example create a for loop that will cycle through the entire array notice i don't do i plus plus at the end but i do i plus equals 4 because i know each time we skip by 4 we have new pixel so first i will be 0 because arrays start from 0 not from 1 then the second will be 4 then 8 and so on let's make our image grayscale there is one strange thing going on with colors we know that any color is a combination of certain amount of red green and blue i'm not sure if you knew but if you take any value between 0 and 255 and assign the same value as red green and blue like this you will get a shade of gray from dark to very light this is not a canvas thing this is how rgb colors work in general so what i can do is take color value for each pixel calculate average and assign the same average value as red green and blue on that same pixel turning it to grayscale i create a constant variable called total which will be a sum of values for red green and blue we are jumping through the array four pixels at a time so scanned data i is always the red pixel value scanned data i plus 1 is always the green pixel and scanned data i plus 2 is always blue then there will be alpha and then we have another red and so on average color value is simply total sum divided by three because we achieved that sum by adding three numbers now i have the average color value and i need to assign that same value to all three colors in this pixel to turn it into grayscale so scanned data i red pixel is equal to average color value variable from line 17 scanned data i plus 1 green pixel is gonna be the same average color value and also blue pixel scanned data i plus 2 is equal to average color value now i reassigned color values in my scanned data array i declared on line 14 and i want to use it and override data array on the image so scanned image variable dot data i set its built-in data array to my altered array with grayscale colors now i use built input image data method which simply paints data from the given image data object onto the canvas i pass it my altered scanned image and i want it to be drawn from coordinates 0 0 we can also manipulate individual color levels like this for example i know there is a css filter property called grayscale that allows us to do this with one line of code but we wouldn't know how to scan image for pixel data and how to go from here to create more advanced particle effects which is exactly what we will do in the next episode this is why i wanted to take you through the basic concept slowly because once you have full mental picture of what's going on here you will be able to better understand the trick i will teach you next time where i make particles move at different speeds based on brightness of underlying image layer it's going to be more advanced than this but i will do my best to help you understand hope you are learning something new today if you do click the like please so now we understand how to draw image on canvas how to analyze it for pixel data and how to manipulate color values of these pixels let's take it one step further and use index of that pixel in pixel array to calculate its x and y position on canvas once we have that information we can do amazing things in this next part i will show you how to create simple particle system and we will make these particles flow over canvas based on data from an image we will make them move faster over dark areas and much slower over light areas we will remove all pixels and recreate the entire image from a stream of flowing particles let's start in index.html by creating html5 canvas element i give it id of canvas1 i also link css stylesheet and javascript file here in style css i use asterisk selector to target all elements on the page and i do global reset to make sure our styles are the same across different browsers margin 0 padding 0 box size and set to body box and let's also do black background i give canvas border 2 pixels solid white and i center it by setting its position 50 from top this will actually only work if i set its position to absolute left will also be fifty percent and transform translate minus fifty percent minus fifty percent will center it both vertically and horizontally i make the canvas exactly the same size as the image i will be using so for you it might be different my image is five hundred times 706 pixels everything else will be done with javascript in script js file i will start by bringing my image into the project so constant variable i call for example my image and i set it equal to new image now i can give it src property which could point to an image file but because some browsers don't like it when you analyze pixels of image with get image data when that image is not hosted on the same server to avoid all different kinds of cross cross-origin resource sharing errors i will convert our image into url string that way the image itself is part of javascript file and everything will work without errors we can use javascript to convert the image but for speed today let's use a website that will do it for us i google something like png to base64 and i choose this link my image can be any image format it doesn't have to be png here i can just drag and drop image i will be using my advice is to make the image as small as possible otherwise you will get a very long data string and since we need to be cycling through these pixels later with javascript smaller image will improve your performance also if you saw the final effect you don't need very fine image resolution for this effect to look great so i made my image as small as possible you can go even smaller if you are having any performance issues i drag and drop it here and i convert it to base 64. for us to be able to use it we need string to start with data colon image so make sure you tick this checkbox here now i copy this extremely long data string which just basically converted the entire image into a line of code so we won't need the actual image file at all i paste that code on line 2 to bring the image into our project now the usual canvas setup const canvas equals to document.getelementbyid and i pass it canvas 1 the id i gave it earlier ctx shortcut for context is equal to this canvas variable dot getcontext2d now we have access to all 2d canvas drawing methods i set canvas width and canvas height to be the same values i declared in style css to make sure the scaling is right now i draw the image on canvas by using built-in html canvas method called draw image it has three versions we will use the middle version of this method that expects five arguments the first is the image i want to draw so my image variable from line one i want the image to start from top left corner of canvas so coordinates 0 0 and i want it to cover the entire canvas area so end coordinates of image rectangle will be canvas with canvas height sometimes drawing your image this way will not work for you because the draw image method only works when image has been fully loaded to make sure we avoid any potential errors we can wrap this whole thing in event listener since we gave our image src property we can now listen for load event on it like this and in callback function only after the image has been fully loaded we run all the code that depends on that image i will just put everything inside like this why not we are now all set let's create a simple particle system that will produce a set of falling particles to flow over the image i create a led variable called particles array and i set it equal to an empty array i use led here so that i can reassign it to a new empty array later if i want to i create a constant variable called number of particles and i set it to let's say 5000 i create a javascript class called particle with capital p we will use it to create 5 000 similar particle data objects and we will push them inside particles array from line 12. constructor is a mandatory method on javascript class and it will contain blueprint for each individual particle this dot x horizontal x coordinate will be a random number between a zero and canvas width i want particles to kind of splash over the image from top so i set start and distance y horizontal coordinate to zero you can also do other things here maybe you want the particles to flow from bottom to top or from left to right or maybe you want them to spiral around in a circle you would adjust starting x and y based on the final effect you are going for falling speed will be 0 at first i will also have velocity which will be a random number between 0 and 0.5 basically here i have falling speed which will be calculated based on brightness of background so particles will fall over black areas very fast and over light areas of the image much slower velocity is here just to give it some additional randomness i think it will look good particles will have random size between 1 and 2.5 pixels for example update method will calculate particle position for each frame before we draw it let's start simply by adding plus one to a horizontal y-coordinate once particles fall below the bottom edge of canvas i want them to reset to position zero up top so they can fall down again at the same time i will also give them different random horizontal x position this is important because particles will fall slower over light areas of the image actually rather than to have all particles fall at the same speed we can randomize it a bit this dot y plus equals this dot velocity and velocity on line 20 is a random number between 0 and 3.5 custom draw method will simply just draw a circle to represent each particle begin path to start drawing fill style will be white ctx arc to draw a circle i pass it x and y size start angle and end angle then i call ctx fill to fill circular pathway color i create a custom function called init shortcut for initialize this function will just have a for loop that will run as many times depending on number of particles variable in this case 5000 each time it runs i call built-in push method on particles array from line 12. push method places whatever we pass to it at the end of the array so i pass it new particle the new keyword will trigger particle class constructor on line 16. it will create one new blank particle object and assign it properties and randomized values based on the blueprint for loop will run 5000 times filling particle array with 5000 randomized particle objects now i call init to fill the array function animate will be our main animation loop first i want a semi-transparent black rectangle to be drawn over the canvas for every frame this will give particles fading trails i set global alpha to 0.05 this just specifies transparency value that is applied to shapes and images on canvas 0 is fully transparent one is fully opaque now i set fill style to rgb 0 0 0 which is black color i draw a rectangle that covers the entire canvas ctx fill rect starting coordinates 0 0 and in coordinates canvas with canvas height i will cycle through the entire particles array and for each particle object in the array i call their associated update method from line 23 and a draw method we declared on line 30. this for loop will cycle through all 5000 particles for each frame of animation it will recalculate their position with update method and it will draw them at their new coordinates with draw method now i just call animate to kick off animation loop this will not work because i also have to call a request animation frame here and i pass it animate request animation frame is a built-in javascript method when i pass it animate the name of its parent function from line 43 that way animate will run all its code and it will just call itself again here on line 51 create an animation loop through a programming principle called recursion when function calls itself over and over that worked now we have falling white particles that leave trials perfect i can temporarily copy draw image call from line 10 and i put it inside animation loop so that we can see the original image for now particles are falling and ignoring the image how do i make them fall at different speeds based on brightness of the area they are moving over one way to do that is to create a brightness map of the entire image it will correspond to x and y coordinates of each pixel then i can run this information against particle's current x and y position and adjust particle's speed based on its current coordinates let's do it step by step first i create a new led variable called mapped image this will be an empty array at first my goal is to have this array hold brightness value of each pixel in the image along with that pixels x and y coordinates so i can compare it to x and y coordinates of each particle and adjust their movement speed accordingly so on the first page load we just draw image on canvas here on line 10. i create a custom variable called for example pixels and i set it equal to ctx dot get image data built in canvas method this method scans area of canvas for pixel information and it will return an array like object that holds red green blue and alpha value for each individual pixel in the scanned area i want to scan the entire canvas so i pass it 0 0 as starting coordinates and canvas with canvas height as the end coordinates so now my custom pixels variable holds whatever get image data returned after scanning the designated area in our case the entire canvas if i count to lock pixels i see this returned image data object if i open it it contains array called data it also contains height and width attributes that correspond to width and height of the scanned area we scan the entire canvas so width and height correspond to canvas width and canvas height from lines 7 and 8. let's inspect the data array you can see it is called uint clamped array that means unassigned integer so it can only contain elements that are whole numbers with no decimal points it also says clamped because these numbers are limited to a range between 0 and 255 if you work with css before you know that in rgba color declaration every color can be declared by combination of red green and blue with values between 0 and 255 for each we established that each element in this data array could only be a number between 0 and 255. if you look at the data structure you can see that every 4 pixels in the array combined create rgba color declaration of 1 pixel in our image so this one for example is 0 for red 0 for green 0 for blue and 255 for alpha opacity in this case alpha is also declared as a value between 0 which is fully transparent to 255 fully opaque we can double check if you are right about this we have an array of 1 million four hundred and twelve thousand elements and we know that each four elements in this array represent rgba color value of one pixel so one million four hundred and twelve thousand divided by four is 350 000. these numbers depend on the size of the image you uploaded so for you it might be different we know the image i'm using is 500 pixels wide and 706 pixels high so 500 times 706 is also 353 000 so that checks out let's look at the pixels with color that are not black for example here we have 111 for red 83 for green blue and 255 for alpha then 139 is right of the next pixel and so on so now we understand how the color data returned by getimagedata method is organized we can cycle through it and save it in our own array and since we know width and height of the scanned area we can calculate x and y-coordinates of each pixel since we know how many pixels fit on one row and we know how many rows and how many columns our image has this is the only a bit more difficult part in this tutorial so if you are a beginner keep in mind this is not easy don't feel bad if you struggle to imagine what exactly is going on i made a beginner friendly version of this technique where we cycle through all pixels and turn image into grayscale sometimes it's easier to first try this with a simple project i will link it in the video description we will cycle through every pixel in this image row by row from left to right starting from the top left corner so the first for loop will have a vertical y coordinate at zero our image is 500 pixels wide so y will be 0 and x will be 0 1 2 3 all the way to 500 then we increase y to 1 and again we cycle through pixels 0 to 500 horizontally then we increase y to 2 and we cycle through the entire x-coordinate of pixels again we will do this 706 times because our image is 706 pixels high which means it contains 706 rows of horizontal pixels the code will look like this we will use nested for loops which means loop inside of another loop the outer loop will represent vertical y coordinate so it will run 706 times because our image is 706 pixels high which means we have 706 rows of pixels we will start with top row 0 and go all the way down to row 706 on the bottom for each row of pixels in the image i will create an array called row it will hold our pixel data for each of the rows for every one of these rows we enter the inner loop and index of this in loop will correspond to horizontal x-coordinate of pixel within the image starting from x-coordinate 0 and going to the right until we reach pixel 500 because our image is 500 pixels wide then we just exit the inner loop increase y by one therefore jump in to the next row and going through the horizontal pixels from left to right from zero to 500 again i'm not sure if i'm over explaining this or if you need me to explain it more let me know in the comments please i'm just trying to make it accessible for beginners as well so now we have two nested for loops that work together to cycle through every single pixel in the image for each individual pixel i want to read its color values so here i will create a temporary variables for red green and blue red for each pixel will be pixels variable from line 11 dot data which is this data object here with 1 million and twelve thousand elements where we know each four elements represent red green blue and alpha of one pixel there are many ways we can skip array index by four i will use this formula i'm sure there is easier way to do this let me know in the comments the first part that says y times 4 times pixel's width just keeps track of overall number of array elements from previous rows and the second part that says x times 4 just adds the items from the current row to it so the final number we get is array position of red value for pixel with this y-coordinate and this x-coordinate this is not programming anymore this is more like advanced logic and visualization i wish i had more visual tools to show you better what's going on i will try to come up with something for future tutorials in the meantime let's just finish this once we know what index red color value is it's easy to get green blue and alpha because we know that this data array is organized in a way where red is followed by green which is followed by blue and alpha since i know this i know that green is whatever red is plus one and blue is position of right in pixels array plus 2. so now i have calculated red green and blue of pixels at this vertical y coordinate and this horizontal x coordinate i could just add them together and divide them by 3 to get their average value which is the same technique we used to turn image into grayscale in the previous episode for beginners link in the video description there is a strange thing about human eye that we don't perceive red green and blue the same the same amount of red is not the same brightness as the same amount of blue when perceived by human eye we can create a simple utility function that will take red green and blue value we just calculated for each pixel it will adjust them by different amount based on human brightness perception and it will return a single number representing relative brightness of that pixel custom function called for example calculate relative brightness it will expect three arguments red green and blue we calculated on lines 20 21 and 22. it will just straight up return a number calculated by following formula i found this formula online we don't have to fully understand it because all it does it adjusts red green and blue by different amounts to make up for the fact that the human eye doesn't perceive these three colors the same when it comes to their relative brightness this is just for fun to make it more visually accurate i guess as i said this formula could have been a simple other red plus green plus blue divided by 3 to get average and it would work as well of course then you have to adjust the rest of the code like particle falling speed based on the range of numbers this would return up on line 23 i create a variable called brightness thanks to javascript principle called hoisting i can call my custom calculate relative brightness function i declared on line 27 up here on line 23 before that function is declared hosting allows us to do that it expects three arguments so i pass it right i calculated here on line 20. this simply is a reference to index in pixels array as we cycle through it so for example for this pixel red would be zero green would be zero and blue would be zero so brightness is zero as well i create a constant variable called cell and i set it equal to an array i give it property called cell brightness and i set it equal to brightness variable from line 23 here inside the cell array you could also create another property called cell color and create rgb color declaration where you concatenate red green and blue and you save this pixel's actual color so we can use it in fill style on draw method for that particle that way you can persist original colors of the image more on that later so we calculated relative brightness of pixel of this vertical y and this horizontal x coordinate and saved it in a custom array i call cell i take row variable from line 18 and i push cell from line 24 to that array we will create this cell array for every pixel in every row so row variable from line 18 will be created times because outside for loop will run for every row of our image which is 706 pixels high and inside of each of these 706 rows we create cell array here on line 24. we will have 500 cells for each row because our image is 500 pixels wide each cell holds relative brightness value for each individual pixel in the image so every time the loop is done and we pushed 500 cells into that row we take the entire row from line 18 and push it into mapped image array from line 16. that way when all these loops have run we have mapped image array with 706 row arrays inside of each of these row arrays there are 500 cell arrays each cell array from line 24 represents one pixel in the image we did it we cycled through pixels variable from line 11 we took that image data and created our own array called mapped image that contains brightness values for each pixel on canvas if i consolidate mapped image you see 706 items these are the raw arrays from line 18. each one holds 500 cell elements from line 24 and each cell is an array that contains only one element that is this cell brightness property that contains relative brightness of each pixel we calculated we will get a lot of zeros as cell brightness value because there are large black areas around my image but if i keep looking i will be able to find other values here as well see now i just need to take this information about brightness i stored in mapped image variable and somehow make particles fall at different speeds based on that first of all we need to navigate in indexes of that array based on particles current x and y coordinates since array index has to be integer whole number with no decimal points here on line 48 and 49 inside particles class constructor i create position 1 and position 2 custom variables that will simply round down this dot x and this.y making sure that these numbers don't have decimal points i also need to put these values here inside update method to make sure every time we update x and y position these values stay rounded down as whole numbers as integers now the main trick of how this entire effect works i will adjust this dot speed from line 45 i will literally take values from my custom mapped image array and since we organized pixel data in the array to the same width and height as our canvas using our nested for loops i pull value from a mapped image array based on particles current x and y position this dot position 1 corresponds to particles current vertical y coordinate as we declared on line 52 and adding it as index like this on line 54 will access custom row array in mapped image which has the same vertical y position as the particle currently has then we go one level deeper inside map image array to access horizontal x position i called these arrays cells if you remember and they contain only one property called cell brightness so that's array position 0. so what we have here is mapped image custom row array from line 18 custom cell array from line 24 and it has only one element i called cell brightness from line 25 so position in this array is zero and since outer loop that created mapped image on line 17 matches height of canvas and in a loop from line 19 matches canvas width the brightness information held in mapped image will correspond exactly with our image let me show you i appreciate this might be hard to follow and imagine what's going on at first if you can't follow what i'm explaining here with no problems congratulations you are not in majority of us it took me a while to realize how this works to create a full mental picture i bet some of you will be able to tell me there is a much simpler way to do this and i am looking forward to these comments i've learned a lot from your comments already so please keep them coming so here on line 54 i take speed from line 45 and i assign it to brightness value of that area in the original image then i create a custom variable let's call it movement for example relative brightness we calculated earlier and which is now reflected in this dot speed because of line 54 with a small number between 0 and 2.5 approximately i think since i want dark particles that have brightness close to zero move very fast and i want light particles with relative brightness close to 2.5 to move very slow i flip it by saying 2.5 which is the maximum minus this dot speed and i also want to randomize it a bit so if we have a large area of particles with the same color they still don't move exactly the same speed to do that i just do plus this dot velocity from line 46 now here on line 57 i say this dot y plus equals movement and we should be able to see that particles react to image now velocity on line 46 is too high let's change it to random number between 0 and 0.5 perfect important part of this effect is that the faster the particles move the more transparent they are on line 77 inside animate i remove the image so it's not been redrawn over and over you can see we are getting something nice on canvas already code between lines 77 and 79 just creates a semi-transparent rectangle that is being drawn on canvas over and over given particles fade in trails once we draw it i want global alpha to increase to let's say 0.2 and here inside for loop that cycles through all particles for every animation frame before we actually draw the particle i will adjust global alpha for that particle to be equal to particle's speed property that's a bit too much let's half it speed times 0.5 up on line 10 on the first page load i draw the original image on canvas using built-in html canvas draw image method on line 11 i call get image data on it to get information about all its pixels and i save it to my custom pixels variable at that point i don't need the image anymore so i just clear canvas deleting the original image when i do this we will never see that image at all it will be drawn analyzed and deleted when page loads are very fast there are a lot of moving parts and many ways to customize this effect you can try to tweak some values and see what it does idea for this effect came from this brilliant code pen link in the video description luis also has many other interesting animations on there you can follow him if you want there is a lot of code in that code pen today we wrote our own version line by line using just plain vanilla javascript and we explained exactly what's going on on each line of code i hope it gave you some value and brought you one step closer to becoming a master of web animation now you should be able to understand it and tweak it to make your own variations or maybe even build on it and create something completely new now we know how to make black and white pixel rain how about we take it to the next level we will add colors give particles a different movement patterns and shapes experiment with filters blend modes gradients and i will try to create fire effect let's see what comes out of that click like please in the previous episode we built this black and white pixel rain effect step by step completely from scratch and i explained how everything works let's just have a play with it and create variations using different javascript tools and techniques let's see what comes out of that in style css i move the canvas slightly more upwards first of all i will quickly swap to a different image we are using data string that contains all image data to replace it i just enable word drop in my code editor i select beginning of the string then i scroll all the way to the end and on my windows pc using vs code editor when i hold down shift key and click here left click it will highlight all the code in between i delete it and replace it with a different image when replacing images in your projects make sure canvas width and canvas height on lines 7 and 8 as well as install css file is updated to much dimensions of the new image you are using in my case i made sure the new image is the same size as the old one i gave you a challenge last time instead of black and white effect to try and dynamically assign colors of the original image to the particles as they flow over areas with different colors solution is very simple we already calculated red green and blue here anyway to get our brightness so i just create another property in custom cell array called cell color and i concatenate string rgb plus right plus comma plus green plus comma plus blue plus closing bracket now this little cell array we create for each pixel contains two values relative brightness and original color to use this color all i need to do is go down here to draw method on particle class and on line 67 instead of setting fill style to white i set it to this new color property inside cell array same as we do with brightness on line 56 i go down two levels inside my custom mapped image array and inside cell array i see that cell brightness is the first property so index 0 and cell color is index 1. down here on line 67 i set the last index to 1 and here we go we are drawing in color that was easy let's play with particle movement now we will face a weird bug here i will tell you why it's happening and i will show you easy way to fix it when i increase vertical y value particles fall down what if at the same time i increase horizontal x position oh here is the bug one of our particles moved outside the canvas area and because of lines 56 and 68 javascript doesn't have indexes for these coordinates in mapped image array and everything breaks in this case i can fix it by resetting x and y correctly on line 61 if vertical y position reaches bottom of canvas i set it back to 0 and i randomize x horizontally i will also check when horizontal x position is more than canvas width and i set x back to 0 and randomize y vertically this solves the bug we are getting when particles move outside the canvas area but for the next effect i want to use trigonometry and make particles move in waves i won't be able to make sure particles never reach outside of canvas area when we have complex flowing movement like that so i will have to fix that back in a different way by the way you can also change angle at which particles fall by adjusting lines 59 and 60 like this first let's try to apply some fluctuation to particles movement and let's see what happens on line 52 inside particle class constructor i create this dot angle property this will be individual for each particle and the reason i make separate angle for each particle is because i want each particle to fluctuate at different speed depending on the brightness of the underlying area just like we did with movement speed earlier let me show you how at first i will increase this.angle inside update method for each particle by one for every frame so far all particles will have identical motion i take that angle value and i pass it to trigonometry function called math.sine this will just take the angle value and periodically run it between plus 1 and -1 over and over which will give us fluctuating movement immediately when i do it i will get an error it is because particles wiggle now and on line 61 i say this.y is movement plus this wiggle created by math.sine the problem is this imagine this particle is all the way to the bottom edge and method sign is added to that position on line 61 making the particle move further down outside the canvas area in reset check on line 63 particles this dot y is not past the bottom edge of canvas yet so it will not reset y to zero but because we added mod.sign to it on line 61 particle's actual coordinate is outside canvas normally that wouldn't be a problem but here on line 57 i calculate speed of particles based on their coordinates because each set of coordinates has different underlying brightness and the speed depends on that my mapped image array on line 57 has only values for actual image dimensions so when i ask it to access vertical index 710 when my original image was only 700 pixels tall this index in mapped image array doesn't exist in this case javascript will not know what to do and the entire animation will stop in console log you will see something like this cannot read property some number of undefined because we are asking for index in mapped image array outside the canvas area and mapped image array is set exactly to match canvas dimensions as we explained in the last episode so this index in that array doesn't exist so on line 57 we only want to assign a new speed value if particle is within canvas so that we can check for brightness of underlying area by accessing associated indexes and mapped image array i will just simply do an if statement here if vertical y position is within canvas area this.position1 and at the same time horizontal x position is not undefined as well only then assign new value to this dot speed otherwise just use all the distort speed value that was assigned in previous frames that's fixed but i get another error on line 76 the same issue here we are asking for color of the original image but particle is currently outside canvas area so mapped image array doesn't have color data for this position i do exactly the same fix if statement only if horizontal and vertical position have corresponding indexes in mapped image array and are not undefined only then change fill style to new color otherwise fill style will just stay the same color from the previous frame problem solved now we can give particles any crazy movement and the effect will not break the wiggle is barely noticeable let's make particles spin around properly i also add circular movement to this dot x i change sine to cosine on line 61 angle is increased by one per frame of animation way too fast look what happens when i slow it down we can actually see the circle movement now but all particles are moving in the same rhythm i put angle property on particle class constructor on line 52 which means each particle has its own individual angle value if i wanted all particles to move the same like this angle could have been a global variable and it would have saved javascript a lot of calculation since we have separate angle for each particle let's make each particle spin around based on its distort speed property which is individually calculated on line 58 based on brightness of the underlying area meaning that the dark areas of our image will create different circular patterns than light areas this dot speed is too fast let's divide it by 10. interesting making it even smaller number will really show off what's going on because particles move slower over light areas just because they spend more time in there they are more likely to make a complete spin and it kind of looks in some places like particles are actually bouncing off the light areas in the image how about we create our own spectrum that's also very easy up on line 9 i create a constant variable called gradient 1 and i set it equal to ctx create linear gradient this is a built in canvas method it expects four arguments two sets of coordinates these determine direction of the gradient imagine drawing a line between them to see the direction i want my gradient to go from top left corner coordinate 0 0 to bottom right corner coordinates canvas width canvas height i take this gradient one variable i just created and i call at color stop built in method on it it expects two arguments position between 0 and 1 and color we want at that position i will just speed this up and make some color stops to create rainbow my color stops will be at 20 30 40 50 60 70 and 80 percent and the colors will be for example pink red orange yellow green turquoise and violet to apply this i can simply go down to line 84 i disable colors from the original image for now and i set fill style to gradient one variable we just created upon line 9 you can change these coordinates to change angle of your gradient what if i want size of particles to be small in dark areas and larger in bright areas very simple to do as well i go down to line 70 inside update method and i set this dot size to this dot speed as speed already depends on brightness anyway how about speed times 2 maybe speed times 1.5 let's comment outline 88 and uncomment this to get original colors how about instead of filled circles we use stroked rectangles this works but we didn't set our stroke style so it defaults to black i just added here on line 87 the rectangles are too small maybe size times five how about size times three interesting you can put any drawing code here to represent each particle even more complex shapes or image from a sprite sheet like we do in game dev videos for example if you use more complex particle shapes you have to consider the number of particles that make up our effect it might be a lot for our computer to handle and draw all these complex shapes let's try to replace particles with letters for example with capital m that seems to run okay even though replacing circle with letter is a bit more demanding on performance how about i create a constant variable called letters and i set it equal to an array the array will contain some letters for example we can spell mandalorian or let's just do n a n then down here in particle class constructor i create a new property and each particle when it is created will be assigned one of these letters randomly i do that by saying this.letter equals letters array i just created and index will be a random number between 0 and letter's length this will give us numbers with decimal points but array has only indexes 0 1 and 2 because i have 3 elements in my letters array to make sure we only get integers whole numbers without decimal points i wrap it in math.floor which will round it down to the nearest integer now i can take this new letter property i just created and in draw method on line 95 inside fill text instead of hard-coded letter m i pass it this dot letter now our particles are random letter from that array up on line 25 i reduce the number of particles so that we can actually make up individual letters better inside animation loop i comment out line 12 that changes particles opacity based on their movement speed and i set everything to full opacity global alpha equals to 1. this looks pretty cool what if i want only certain percentage of my particles to be letters and the rest regular circles how do i control that i can for example create this.random property on particle class constructor and i just set it the math at random like this math random code like this returns a random small number between 0 and 1. each particle gets assigned a different random number at the point when the constructor gets called and that particle is created on line 96 inside our custom draw method on particle class i can say if this dot random is less than 0.1 which should roughly be in 10 percent of the cases then draw particle as text as one of the letters from letters array else draw it as a circle this way you can control different percentages of particles and give them different properties based on that you can do anything here if i change 0.1 to 0.5 now approximately 50 of my particles should appear as letters i can use ctx font to set text size and font family let's try 20 pixels aerial i can tweak the amount of text particles by tweaking if statement on line 97 i can replace text with stroke rectangles make them larger okay back to the circles this effect also works with global composite operation property where you can blend particles together in different ways i like when particles flow from one side and wash over the image until now it only ever happened on the first page load if i set start in x or y position to edge of canvas what if i want particles to come in waves and periodically create this splash movement let's create a variable that will switch between two values i will call it for example switch oh no that's a reserved word in javascript let's do switcher and another one called counter both will be let variables because i will be changing their values i create set interval that takes two arguments callback function to run and how often in milliseconds to run it i want it to run every 500 milliseconds every time it runs i increase counter plus 1 and now i can use that to set periodic events in my code base i say if counter modulus 12 is equal to 0 modulus operator percentage symbol is also called remainder operator this if statement means if counter value is divisible by 12 with a remainder of 0 which means as counter increases by 1 every 500 milliseconds this if statement will be true at 0 12 24 36 and so on every time counter variables from line 20 increases by 12 set switcher variable from line 19 to its opposite value switcher times equals -1 at first switcher is set to 1 so 1 times -1 is -1 then it runs again switcher is minus 1 so minus 1 times minus 1 is plus 1. it will just repeat and switch between plus 1 and -1 over and over every 12 cycles of this interval on line 84 inside custom update method on particle class i can say if switcher is plus 1 do something else do something else let me show you what global composite operation does i can set it for example to soft light oh i actually misspelled it it's supposed to be operation you can see these values control compositing or blending mode operations on canvas they can blend shapes adjust contrast hue brightness and other properties of canvas giving us different effects there are over 20 different values soft light affects how light and dark is displayed luminosity preserves luma or brightness of the top layer and it also preserves you and chroma of the bottom layer from their official descriptions it's really hard to guess what will the final result look like so i just try and see what happens some of them like source out might freeze your browser because what it does is performance intensive when you have so many particles like we do in this effect so be careful i can use my custom switcher variable to switch between two different blend modes periodically just for fun combining two specific ones might create something interesting let's swap between luminosity and lighter at the same time i say if counter modulus 10 is 0 every 10 cycles of counter variable set x and y to zero so particles splash over the canvas over and over what if i randomize it this way let's reset y to zero and leave x as a random number now the image automatically switches between two different blend modes and particles periodically reset so they can flow over the image in a wave again let's comment out this bit i can swap sine and cosine on lines 94 and 96 this will affect the circular path particles take tweaking anything on these lines will affect particle movement in a major way i can for example make particles move upwards if i do that i need to adjust reset check on line 96 i'm just playing with the reset checks trying to get it right on line 67 i can change particle size actually no because i am overriding it on line 82 because i wanted particle size to be relative to particle's current speed i'm trying to build a fire effect now i need to get the right size here install css i apply css filters i will use blur 2 pixels and then i sharpen it with contrast contrast is just a number without pixels adjustment size on line 85 will affect how the fire looks like what variations can you come up with do you have any ideas how to tweak this effect and create something interesting let me know we can also use pixel array to turn text into particles in the next creative coding experiment i will show you how to create interactive particle text effect in three steps first i will create a set of particles randomly spread around canvas we will give them some physics i will try to give their movement some elasticity and physics by applying forces of friction and acceleration when they interact with mouse then in the next step i will write some text on canvas i will scan it with get image data and i will make particles take shape of these letters as the final third step i will apply constellations effect that was made famous by particles.js library but i will use no libraries we will write all the code using just plain vanilla javascript to achieve constellations effect we just have to calculate distance between each particle and connect particles that are close enough to each other with lines i think this effect looks really good what do you think let me know in the comments if you get stuck and need some help feel free to reach out to me on twitter or message me somewhere on my youtube channel i do a lot of creative coding experiments canvas animations and game development click like please in index.html i'll link css stylesheet create html5 canvas element with an id of canvas1 and ilinkscript.js file which will contain all javascript logic in style css i just do the basic reset to make sure the page appears the same in all browsers ask the risk selector to target all elements on the page and i set margin to 0 padding 0 and book size into border box border box means that elements padding and border will be included in its total width and height on the body element i set overflow to hidden to hide potential scroll bars and give it black background i choose to place the background on the body element instead on the canvas because this will allow us to use liquid or distortion svg filters if we choose to especially liquid filter only works if canvas background is transparent canvas will have position absolute job 0 left 0 everything else will be done with the javascript in script.js file i create a variable i call canvas and i set it equal to document getelementbyid canvas1 ctx shortcut for context is equal to canvasgetcontext2d doing this will create a built-in context object that contains all kinds of 2d drawing methods that come built in in any browser using canvas api now i can call all of them using our ctx variable canvas width is the window in a width and canvas height is the window in a height this will make sure the canvas covers the entire browser window i create a variable called particle array and i set it equal to an empty array this will contain all our particle objects that each contains information like size color and coordinates and we will pull this information from here to draw them on canvas before we do that i will write some code to handle mouse interactions i create a custom variable i call for example mouse and i set it equal to javascript object by using curly brackets like this i give this custom object some properties x will be the current position of mouse on horizontal x-axis y will be positioned on the vertical y axis radius will be size of a circle area around the mouse within which the particles react to mouse i create an event listener it takes two attributes type of event to listen for and call back function to run every time that particular event occurs i want to listen for mouse move event and every time user moves the mouse within the browser window i want to run the following code callback function on event listener has one special thing about it it has access to built-in object that contains all different details about the event that is occurring all i need here are x and y coordinates of the event so that i can assign them dynamically to our customer mouse object i created on line 8. i need to assign them to the custom object to pull that coordinate information outside of event listener so i can use this information all over our application so as an argument to the callback function i write down event some people just put letter e here it doesn't really matter javascript knows this variable will stand for that built-in event object and will let you use it whatever you call it here as long as you refer to it using the same variable name later on so inside the callback function i set mousex which is property declared on line 9 to event.x and mouse y to event.y if you console.log event inside this event listener you will notice it is quite similar to our custom mouse object on line 9. the only difference is that it contains many more properties and it is only available inside an event listener that's why i am pulling the information from it here and sending it outside to our customer mouse object so that mouse coordinates are available all over our application wherever i might need them later on i can check if it works by console login mouse x and mouse y now if i move mouse over the canvas with open console i can see we are getting values great this is one of my favorite ways to handle mouse input it is useful for everything from generative art website interactions to games now let's draw some text on canvas ctx font properties sets the current font properties for text context on the canvas you can also set bold or italic here if you want then you put size and the last attribute will be font family let's just try 30 pixels verdana for start now i call ctx filtex method which will just draw and fill text on canvas it expects at least three attributes the first one is the text we want to write i just put capital letter a for now the second one is x coordinate on canvas where you want to draw that text the third attribute is y coordinate there is also an optional fourth attribute where you can define max width of the text in pixels we will leave it empty for now but keep that in mind by default the text will be black unless you set it to any other color using fill style property so i call ctx fill style method and set it to white now we can see we are drawing our text at specified coordinates i can change it by adjusting its size on line 21 or its position on line 22. now what i want to do is to scan a small area of canvas around the white letter a analyze the pixels filter out pixels with no opacity because that's where the text is and only save those pixels that are not transparent into our particles array more specifically i want to save their x and y coordinates and i can also save their color values if i want to that way we can replicate the shape of letter with particles by using x and y coordinates of each pixel as coordinates for our particles remember that the black background is on body element so canvas is completely transparent with only letter a in white color to do this i just use another canvas method called get image data we used it before to turn image into particles in one of my all-time most popular videos and we will do something similar today with text official definition of this method is that it returns image data object representing the underlying pixel data of a specified portion of the canvas if i want to say it much simpler you give this method four coordinates for rectangular area somewhere on canvas it scans that area pixel by pixel and it saves coordinates and color information for each pixel inside of a huge data array since we know how the data is organized we can easily extract that information and use it for anything we want so i create a variable i call for example data and i set it equal to ctx.getimagedata and i pass it for attributes to tell it what part of canvas we want to scan i want to start from top left corner so coordinate 0 0 and i want to scan just small bit let's say square of 100 x 100 pixels so i pass it with 100 height 100 which is somewhere around here if i want to visualize the area we scanned i can just stroke white rectangle there so we can see stroke style set to white stroke rectangle and i pass it the same area i passed to get image data method on line 25 see this is the area we will be scanning so our data variable on line 25 will contain array with coordinates and color information for each pixel inside this small square okay let's delete this it was just so we can visualize what's going on now i need a way to create many similar particles that will each represent one interactive dot in our final effect for that i will use javascript class i call it particle with capital p each class has one mandatory method called a constructor constructor will run just once for each particle it will create a new blank object and assign it properties and values based on information i will declare inside of it it will serve as a blueprint our constructor will expect just two attributes x and y coordinates which will be different for each particle and we will pass them to the constructor from the outside to make it spell out our letters for the final effect inside i just say this dot x property on a new blank object you are creating is equal to x we passed as an attribute same for vertical y coordinate we will have other properties but these will be set or randomized here inside the constructor we don't need to add them from the outside so we don't need to pass them as arguments on line 26 this dot size will be radius of each circle particle so let's try for example 3 pixels i will also set this to base x and set it to this dot x and this dot base y and set it to this dot y base x and base y properties will hold the initial position for each particle from the first time they appeared the reason for that is that we will be moving and pushing the particles around by changing their this dot x and this.y properties but i want each particle to remember where it came from so it can return to its original position when mouse interaction ends also i choose to set this.basex equal to this dot x property from line 27 instead of setting it to x attribute passed on line 26 it will be the same but doing this will allow me to modify the starting position of this dot x on line 27 like this and our base x variable will remember that modified state it can be used for interesting distortion effects not sure if we do it today but there is no harm if we set up our code base for it anyway distal density will be a random number between 1 and 30. it will determine how heavy our particles are and how fast they move away from the mouse i want each particle to move slightly different speeds to simulate some physics so now the blueprint is complete we will also need a custom draw method each of our particles will have access to it because it will be sitting on the particle class this method will just take x and y and radius from the constructor and draw a circle there representing our particle i set fill style to white we are drawing a circle on canvas so i need to use ctx begin path method think of it as pencil touching down on canvas to start drawing i call canvas arc method and i pass it this dot x and this dot y coordinates this dot size will be radius of the circle start angle will be 0 and end angle will be math.pi times 2 which is value in radians that represents that actually converts to 360 degrees so we have just drawn a circular path from 0 to 360 degrees which is a full circle if you change start angle and end angle values here you can use arc method to create half circle or even many other shapes i call close path and ctx fill to fill the path with white color now i create another custom function i call init its job is to take particle class we just created and call it many times the fill particle array with randomized particle objects first i will set particle array to an empty array in case it's not empty at the moment now i call push method which will take whatever we pass to it and pushes that to the end of the array we call it on i pass it new particle the new keyword is a special command in javascript it will call constructor of its associated class in our case it will go up to line 26 create one new blank object and give it values and properties as described in the blueprint between lines 27 and 32. on the line 26 we can see our constructor expects x and y coordinates as attributes so here on line 45 i pass it 50 50 as coordinates for now now i call init to push one particle into the array i count lock particle array to see if it works i need to comment outline 17 that's still log in our mouse position yes now i can see the array contains one particle i can add another one at coordinates 80 and 50. and now we have two particles in our array i create a custom function i call animate which will handle animation loop first i call ctx clear rectangle and pass it canvas dimensions to clear the entire canvas between every frame of animation i create a for loop that will cycle through the entire particles array and for each particle object inside that array it will call its custom draw method we declared on line 34. the last thing i need to do is to call requestanimationframe and pass it animate the name of its parent function this way animate will run all of its code and it will call itself again over and over create an animation loop so far i have just declared animate i also need to call it to start our animation loop it is not drawing our particles and in console i can see we have error on line 37 where i misspelled arc fixed now we are drawing two particles as i declare them on line 45 and 46. i can change their x and y coordinates and you can see they will move around as i change these values so instead of calling the particles individually like this i create a for loop that will run for example 10 times i comment out the two individual particles and inside the for loop i do the same thing particle array push and i pass it new particle it expects x and y coordinates so i will create a temporary x variable its only job is to generate random number between 0 and 500 i do the same for y coordinate now i call new particle and i pass it temporary x and y variables i have just created on line 46 and 47. you can see we are drawing 10 randomized particles because for loop on line 45 runs 10 times if i want the particles to be randomly spread all around the canvas i can set x to a random number between 0 and canvas width and temporary y variable to a random number between 0 and canvas height now our entire canvas is covered with particles i increase the amount of particles to let's try 500. well done we have base structure for our generative art project in place first we declare global variables then created object to store mouse coordinates then on line 25 we create a blueprint that will be used to create particles function init online 43 will use that blueprint to fill particle array with particle objects and lastly we created animation loop on line 56 which is running over and over and redrawing our canvas for every frame even though it looks static now we are actually drawing the same thing over and over let's make it move now on line 35 i can change color let's try red i create another custom method i call update its job is to calculate distance between current mouse position and current particle position if they are close enough i want particles to be pushed away from the mouse now i will show you how to calculate the distance between two points this can be used for creative coding and it is also very useful for games let's calculate distance between two points on the horizontal x-axis first i will call this variable dx it is simply the difference between their position on x-axis let's say if mouse position x was 50 and particle position x was 30 dx will be 50 minus 30 which is 20. simple is just the difference same for dui which is the difference between position of two elements we are comparing vertically we are comparing mouse against all particles one by one so d y is mouse dot y minus this dot y this dot y stands for each individual particle as we cycle through all of them one by one imagine these two values we have just calculated create a right angle triangle between mouse and particle we have dx side we have a d y side of the triangle and we know there is a right angle between them so 90 degrees we can get distance between these two points by calculating the longest side of the triangle opposite to the right angle it is also called hypotenuse there is a built-in method hypotenuse method in javascript but today i will just use pythagoras theorem to calculate it we have two sides of right angle triangle and we know there is a 90 degree angle between them we need to calculate the third side and that will be the distance between two points which is what we need formula is mod square root dx times dx plus dy times dy which also is which is the same thing as mod square root dx squared plus dy squared since this is calculated inside update method on particle object each particle will always be aware of how far away it is from a mouse so here i can say if distance between particle and mouse is less than 500 run this code if particles are closer to mouse than 500 i will set their size to 50 pixels else digital size will return back to 3 which is the original value we declared on line 29 to run this code i need to go inside animation loop and on line 68 inside for loop that cycles through the entire particles array for every frame of animation the same way i call draw method for every particle i will also call update the method we have just created and you see it works now i can change the distance particles react to mouse here on line 45 i can also create more particles on line 55. now you can see the distance is being correctly calculated and we have a nice boilerplate for creative particle effect we can do so many things now maybe if you want before we continue you can save and copy this code base in a different folder and experiment with it later on it's always good to have the base setup stored away somewhere and now you can just focus on your experiments so what i want to do now is for particles that are close to mouse to start moving away from it i want them to move from the center point of the circle towards the edge i also want particles to move at different speeds depending on their weight we defined earlier when we assigned this density property inside particle class constructor and third thing i want to happen is for particles to move fast if they are close to mouse but gradually slow down the closer they are to the outer edge of circular interaction area until they reach speed of zero when they touch the outer edge basically we will introduce some basic physics to particle movement this might sound like a complicated thing to do with code but i will show you a nice calculation that can do all of this for us i create a variable i call force direction x and i set it equal to dx divided by distance force direction y will be d y divided by distance on line 48 if particles are closer to the mouse than 300 pixels i will add force direction x to particles current x position and force direction y to particles current y position this will just make them slowly move towards the mouse if i multiply these values by let's say 3 it will make the movement faster now i want to set a distance past which particle's movement speed is zero so i create another variable i call it max distance it will be equal to mouse.radius we declared on line 11. any particle that is more distant from the current mouse position than this will stop moving this calculation i'm about to do takes any range of numbers and converts it to a range between 1 and 0. it will convert values between 0 and max distance into a range of 1 to 0 just so i can multiply values on line 51 and 52 by this number and get the particles to slow down as the distance between them and mouse increases until they reach speed 0 as they reach the outer radius of interaction's circle around the mouse so i will call that variable for example force all i want to do now is to take number range of 0 to 250 and convert it to number between 1 and 0 depending on where on range between these two values the number is 0 is particle with a distance zero from the mouse so it will be touching the mouse i want this number to be converted to one so i can multiply current particle speed by this which will make the particle move at its full speed the second side of that range is number 250 which is represented by max distance variable i want any particle that are further away from the mouse than this to have speed 0 so they stop moving let's say there is a particle halfway between 0 and 250 so this particle is roughly halfway between these values it is 125 pixels away from the mouse at that point i want that particle to be moving at 50 speed so i need force variable to be equal to 0.5 at that point if i multiply current speed times 0.5 it will move at 50 speed when particle reaches 80 percent towards the outer edge of interaction circle i want it to move at 20 speed so i want force to be 0.2 when particle is at 90 percent i want it to be moving only at 10 percent of its original speed and so on to get this behavior i need to convert range between 0 and 250 or whatever max distance variable is on line 48 i want to convert this range to a smaller range between 1 and 0. once i have that value i will multiply it times particle speed and it will cause particles to slow down in proportions to its current distance from the mouse you can already see the calculation i say max distance minus current distance to see how far particle currently is from the max distance area and once i have that value i divide it by max distance again which will tell me what proportion of max distance it is quick example if max distance is 100 pixels and particles current distance from mouse was 20 pixels max distance minus current distance would be 80. then 80 divided by 100 is 0.8 so our force would be 0.8 basically anything multiplied times 0.8 is 20 percent less so it will slow our particle down by 20 percent i'm not sure if this is just me or this is quite difficult mathematical operation to understand it took me a very long time to work this out i will get back to this again and try to explain it better next time we use it let me know in the comments if it's unclear and we can talk about it so now i have the force which is the multiplier that will be slowing particles down as they get further away from the mouse i create a variable called direction x which will combine all factors that play a role in particle's movement speed direction x is equal to force direction x on line 46 times force we just calculated on line 49 times this dot density from line 33. i add this to density into the mix because i also want individual particles to have different mass and i want that value to affect their movement speed it will look more natural otherwise all particles will move the same speed as particles around it i do the same thing for direction y it will be equal to force direction y from line 47 times force which will cause it to slow down as it moves further away from the mouse times distal density which is individual and different for each particle it's a random number between 1 and 31 as we declared on line 33. now on line 53 i say if distance is more than mouse radius which we declared on line 11 this dot x plus equals direction x variable from line 49 which combines all forces that pull and drag on our particle and i do the same thing for particles word and i do the same thing for particles vertical y position this dot y plus equals direction y that was calculated on line 50. it works yay particles move slow at first and speed up as they get closer to the mouse the only problem is that i want them to be pushed away so i change plus sign to a minus sign on lines 53 and 54. yes this is good and this is how you implement quite complex physics with javascript i really like the movement and we will enhance it even further by the way this was the difficult part i am not sure why i put mouse radius here on line 17 inside mouse move event that's wrong i will just delete that radius is set to 450 pixels on line 11. if you look closely particles move at full speed when they are close to the mouse and when they get close to the outer edge of interaction circle they move slower and slower until they stop i can make them move faster by changing density let's try range between 50 and 200. you can change the movement speed by adjusting the range of values here on line 31. so on line 51 i check if distance between particles and mouse is smaller than a certain number defined in mouse radius if it is i push them away from the mouse as you can see particles stay where they are when mouse interaction ends i would like them to return to their original position so here on line 54 in else statement i say if this.x is not equal to this.basex basex is defined on line 29 and it captures the initial position of each particle so if this dot x has changed because we pushed particles around and it is no longer equal to base x which is particles original position i will calculate distance between this dot x which is particles current position and this dot base x which is particles original position on the horizontal x axis once i know the difference i can just return particle to its original horizontal position by saying this dot x minus equals dx but to make particles move back slower i say dx divided by 10. it will eventually get to its original position because this code is run for each frame of animation it will just get there slower also keep in mind we are currently inside else statement which will be entered only when particle is not close enough to the mouse so particles will only try to return to their original position once mouse moves far enough from them so this way we corrected particles horizontally we can also correct them vertically notice i don't do else if here i just do simple if statement because i don't want these to be mutually exclusive if particle has been displaced on x-axis and also on y-axis both of these statements will run for each frame of animation but also they can just run separately in case on the x axis or only y axis position needs to be corrected to return particle to its original position as we saved this original position in base x and base y variables so if this dot y is not equal to this dot base y i calculate d y value distance y value and i adjust this dot y by saying minus equals d y divided by 10. by changing values on line 57 and 61 i can change how fast particles return to their original position so this all works at the moment we are just spreading particles randomly around the entire canvas area by calculating random x and y values here on line 70 and passing them as attributes to particles class constructor on line 72 how do we make the particles spell out letters of text for that we use data variable we declared on line 22. here we call canvas image data inbuilt method that takes four attributes for a rectangular area of canvas we want to scan and it returns image data object that contains information like coordinates and color of the pixels from that area on canvas actually let me rename this variable for clarity i rename data to text coordinates so now this variable i called text coordinates holds whatever get image data method returned i go down inside our custom init function which at the moment runs a for loop thousand times each time it runs it calculates random x and y position and creates a particle there i delete all this code because we don't want our particles to be randomly distributed around canvas we have just scanned the part of canvas where we drew some text and now i will filter that returned object that resulted from our scan and i will remove all pixels with transparent background leaving only data points for pixels that contain color which will be coordinates for particles that make out our text effect keep in mind that canvas is transparent the black color you see is set on body element so the only pixels with color are the actual letters let's go through it step by step i create a for loop and i will do something a bit weird instead of just declaring one variable here as the first attribute i declare two variables i say let y equal 0 comma y2 equals text coordinates dot height as long as y is less than y2 y plus plus this is almost a normal for loop two things to mention here first yes you can do this in javascript you can put more than one variable inside a for loop also i could have just declared y2 variable outside the for loop and it would work as well second thing is that i say y2 equals text coordinates height text coordinates holds image data object that resulted from a scanning small portion of canvas using get image data method this object has height property in pixels and since i want to scan the resultant pixel data line by line in this case row by row i will run this for loop for every row of pixel data we have so we declared two variables and i say as long as y is less than y2 y plus plus don't overthink this we just have two variables y will keep increasing until it reaches the same value as y2 so our for loop will run until that happens so this is analyzing pixels row by row each time i enter row of pixels i want to go through them one by one from left to right so i create a nested for loop which simply is loop inside of another loop here i say let x equal 0 x 2 is text coordinates width which is simply just width of scanned area in pixels in this case we scanned rectangle 100 times 100 pixels as you can see on line 22. so text coordinates height is 100 text coordinates width is 100 as well this outer for loop will run 100 times and each time it runs it will run in a for loop 100 times so we're gonna analyze 100 times 100 10 000 pixels i will explain this further in a minute back on line 70 i say as long as x is less than x2 x plus plus this nested for loop might look complicated but all that's happening here is i have a grid of 100 times 100 pixels i go through them row by row or 969 each time i enter one row i go through that row of pixels one by one until i reach the end in this case pixel number 100 in for loop on line 70 will exit we jump back to the outer loop on line 69 y will increase by 1 which will give us another row we enter in a loop on line 70 again and go through all the pixels on that new row from pixel 1 to pixel 100 then we exit the inner loop again enter the outer loop increase y by one which gives us the next row and we do this for all 100 rows analyzing all 100 pixels in each row as i said we have 100 times 100 pixels because that's the area of canvas i have decided to scan using get image data method online 22. this nested loop basically allows us to cycle through every pixels in that hundred times 100 pixel area for each pixel i check if opacity of that pixel is more than 128 i will explain what that means in a second text coordinates custom variable holds built-in image data object that was returned by get image data javascript method this object has built-in data property which is an array of elements that represent pixels from the scanned area of canvas we can look inside by console login text coordinates in the console i can see it holds image data object if i open it you can see that object has a data property and this data property holds data array with 40 000 elements more down you can also see that it has height and width properties set to 100 which are the values we used in the nested for loops don't get scared this is very simple it is not a normal array if you look closely you will see this type of array is called something like uint8 clamped array if i simplify this it's a special type of array that can hold only numbers and those numbers are limited to a certain range you can say they are clamped each element in this array can only be a number between 0 and 255 if you work with html and css you have seen rgba color declaration which looks like this any color can be created by combining different amounts of red green and blue and the range for each color is from 0 to 255. one weird thing about this clamped array is that it also handles alpha or opacity this way so 0 alpha is invisible 255 alpha is fully visible this is different because in rgba color declaration opacity is a small number between 0 and 1. so why does this array have 40 000 elements when the area we are scanning is 100 times 100 pixels and we know hundred times hundred is only ten thousand not forty thousand also remember that this is a special type of array called clamped array so every element it contains can only be a number between zero and two hundred and fifty-five again very simple explanation let's look back at our rgba color declaration each color is made out of four properties red green blue and alpha same with this clamped array data object the way it's organized is every four elements in that array represent these four color values for one pixel so color for pixel one is the first four numbers in this array color for pixel 2 is the next four numbers and so on that's why we have 40 000 numbers in this array and we only have 10 000 pixels it's because we need four numbers to make one color each number is something between 0 and 255 including the number for alpha opacity that's why online 70 i check if value is more than 128 which would be around 50 opacity it's like halfway between 0 and 255 roughly 50 any pixels with more than 50 opacity will be added to our particle array each one will create a point in the final font shape to extract these values we have our nested for loops if particle passed check here inside if statement i create a temporary variable i call it for example position x and i set it equal to x which is the variable from line 70 and it represents number of pixels in a row that passed our opacity check i do the same for position y which will be equal to y from line 69 which represents a row vertical position of that pixel we are analyzing right now so in terms of x and y coordinates it's a vertical position of pixel that passed our opacity check so i cycle through all pixels i check their opacity value and if their opacity is more than 50 i capture their coordinates in temporary variables position x and position y and i call push on particle array which is my custom array i created to hold particles for my effect push method just takes whatever we pass to it and places it at the end of array so i pass it new particle which we'll call particle class constructor that uses es6 class blueprint we created earlier and creates a blank object and assigns it values and properties based on the rules we declared in the blueprint our particular class constructor expects two values to come from the outside for x and y coordinates so i pass it position x and position y variables from lines 72 and remember that this code runs only when opacity is more than 50 so only these particles from the scanned area will be added to our array there is just one more little problem to sort out as i said clamped array that is held in text coordinates data object contains four elements for each pixel we have ten thousand pixels but this array has forty thousand elements it holds a value for red green blue and alpha for each pixel right now i am just checking one pixel because i passed index one in that array inside square brackets here what i want to do is to cycle through the entire data array and take every fourth value because i know every fourth value stands for opacity for each pixel it's always red value green value blue value opacity value and so on so i need to replace one with some code that will allow us to skip three elements check the fourth one skip another three elements check the next one and so on one way to do this is i say in brackets y times four times data with plus x times four plus three remember that we are inside a for loop so y will be one then two then three as we cycle through rows and x will be one two three as four loop cycles through pixels in each individual row this calculation is not very intuitive just know what i'm doing is i'm cycling through 40 000 elements and always skipping three and checking the fourth one i am not a math expert so this took me forever to understand but i think for now it's good enough for us just to show just so you know what i'm doing i'm sure there are simple calculations to lock every fourth item in an array anyway i will explore it more when we use this technique next time that's it this is how you scan portion of canvas filter out pixels with no opacity and save their coordinates in your own data array since the font we scanned is very small as you can see here we don't want our particles to cover this small area we want to spread them out evenly while keeping the shape of our letter a so here on line 74 i just multiply position x times 10 and position y times 10. i made a small mistake on line 71. earlier i renamed data to text coordinates but here i still refer to it as data let me fix that and we are drawing particles in the shape of a letter a well done this is some advanced coding i can change spread of the particles and therefore size of particle text by adjusting values here on line 74. since we are taking particles from certain area and multiplying their coordinates to spread them around it's not that easy to move the text left and right but i can still do that by adding adjustment variable here on line 72 and also on line 73 i call them adjust x and adjust y and up here on line 6 i declare them and let's push x 10 pixels to the right and why 10 pixels down now you can move your particle text shape around anywhere you want by adjusting these two values size of the effect can be changed here on line 76 let me just center it in the middle and of course you can change the text we draw here on line 23 some of you might like this effect already but that's not all i also want to connect particles that are close enough with lines similar to the famous particles.js constellations effect but on a text instead on random floating particles and instead of using a library we will write our code ourselves i create a custom function i call connect its job will be just a cycle through the array of particles that make up our letter and measure distance between each particle if they are close enough we will take coordinates of the first particle and we draw a line to the coordinates of the second particle that's it click the like please if you're getting value from my tutorials so i create a for loop that cycles through the entire particles array since this for loop will have to run for every frame of animation let's optimize it a little bit normally i would just do nested for loops and i would compare every particle to every other particle in the same array but i don't have to do that the inner for loop only needs to cycle through the remaining particles in the array because all the particles before have already been compared in previous cycles of the outer for loop i'm not sure if i'm explaining it clearly but let's write some code and i will try again so in the inner nested for loop instead of cyclin through the entire particle array again just so i can compare every particle against every other particle i will only cycle through particles that have a higher index than a because anything with lower index has already been compared in previous cycles of the for loop so here i say let b is equal to a as long as b is less than particles array length b plus plus if you look at line 96 again when the outer loop runs for the first time it will cycle through the entire for loop in the outer loop and inside the inner loop as well but eventually when variable a in the outer loop reaches number let's say 50 in the inner loop we say b is equal to a and we cycle only through particles from index 50 to the end of the array this will pair up all individual particles inside particles array all i need to do here is calculate their distance and say if distance is less than some value connect them with a line we have already calculated distance between mouse and particles on line 45 so again it's exactly the same thing dx variable will hold distance between two elements we are comparing on the horizontal x-axis the ui variable will hold distance between two points in this case between particle a and particle b on vertical y axis this will give us two sides of right triangle and we know that the distance between these two points is hypotenuse the longest side of the right triangle the side that is opposite to the right angle to calculate it we can use pythagoras theorem formula which we learned in school a long time ago math square root from dx squared plus d y squared or in other words math square root from dx times dx plus dy times dy so to calculate dx distance of two particles on the x-axis i say particle array a x minus particle array b x a and b variables will be changing as we cycle through both for loops so this code will compare every particle against every other particle by the time the for loop has finished d y will be particles array a y minus particles array b y now we have two sides of imaginary right triangle and all we need to do to get distance between these two particles is to calculate hypotenuse so math square root dx squared plus d y squared now i can say if distance is less than 100 pixels i set stroke style to white because we want white lines line width will be 2 pixels for example on canvas line is the same as arc we used to draw circle it is considered to be a path so to start drawing i will have to call begin path to draw a line on canvas you need to call move2 method to set starting x and y coordinates and line 2 method to set target x and y coordinates when you do that a line will be drawn between these two points each point defined by two coordinates x and y so at first i call a ctx move to method which creates a starting point of a new path at coordinates we pass to it we are inside an if statement that only runs when two particles are close enough to each other so i just want to draw a line from one particle to another the starting point of the line will be x and y coordinates of particle a that passed our distance check then i call a line 2 inbuilt canvas method this method simply said creates a straight line from coordinates we pass to it to the last previous point inside the same path start and end of path on canvas will be defined by beginpath and close path method calls here so i call line 2 and i pass it x and y coordinates of particle b that passed the distance check now that you kind of understand how parts and lines work you might be able to see that we can keep drawing more and more paths like this and create custom complex shapes but i only want to draw a single line connecting particle a and particle b all you need to do to create the popular particles.js constellations effect with vanilla javascript is this code we just wrote from line 94 inside our custom connect function we have just declared a function we also need to call it inside our animation loop to actually run the code so on line 90 i call connect and here we go this is just the basic effect we can make it a little bit nicer than this here on line 105 you can change the distance limit the limit will determine maximum distance between particles that get connected by white line i think 50 looks pretty good but for you it might be slightly different number depending on your base font and how much you decided to scale your particle shape earlier just try different numbers here and see what you like one important visual thing that particles.js library does is that the lines are not always fully visible they don't just appear and disappear as particles move closer and away from each other lines slowly fade away and slowly appear again basically opacity of the lines is dynamically calculated based on the distance between particles it looks much cleaner that way to do this in our custom code base is very simple on line 96 i create a new variable called opacity value and initially i set it to 1 full visibility then here on line 107 i set stroke style to rgba color declaration rgb value for white is 255 255 255 and i will concatenate opacity value variable as the last fourth attribute for opacity like this now we have a variable that can dynamically change an opacity of lines will change with it to make opacity proportionate to the current distance between particle a and particle b i run this strange formula basically what i want is if distance is close to zero particles are very close to each other so i want opacity to be close to one as the distance between particles increases i want to decrease the opacity value until particles are far enough and opacity will be 0. there are many ways to do this simple way is to say 1 which is full opacity full visibility so lines are visible minus distance divided by 50. notice that i divided by the same number don't overthink this let me give you two examples if distance is zero particles are close line is fully visible on the other hand if distance is 50 1 minus 50 divided by 50 is 1 minus 1 so 0 lines are invisible when the distance between particle a and particle b reaches 50 or whatever you set the limit to if you want particles to have zero opacity when they reach maximum distance keep these two numbers on lines 106 and 107 the same if you change distance to 100 change the formula on line 107 200 as well and the effect will work the same lines will still disappear when they reach or get very close to maximum distance here i am struggling a bit because i didn't notice i forgot one comma on line 108 inside rgba color declaration just bear with me a bit nothing is changing about the code i'm just trying to find the problem now i notice the missing comma and suddenly everything works what i will do now i will copy these two lines and place them outside the if statement which thinking back was not necessary because lines are only drawn if we enter the if statement on line 107. now i just restore the formula to dynamically calculate opacity on line 105 and the base effect is a ton there are many tweaks that can be done now to achieve different results hope you had fun coding with me today if you did let me know what you've built html canvas is a powerful tool and it allows us to do so many things these days good luck with your coding studies i hope to see you again for more creative coding experiments
Info
Channel: freeCodeCamp.org
Views: 174,878
Rating: undefined out of 5
Keywords:
Id: UoTxOVEecbI
Channel Id: undefined
Length: 119min 22sec (7162 seconds)
Published: Tue Mar 23 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.