Creative Coding Deep Dive for Beginners | Frank Dvorak | Skillshare
Search

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Creative Coding Deep Dive for Beginners

teacher avatar Frank Dvorak, Creative Coding

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Introduction

      0:39

    • 2.

      HTML & CSS setup

      1:20

    • 3.

      JavaScript setup

      2:08

    • 4.

      How to draw rectangles

      1:30

    • 5.

      How to draw lines

      3:10

    • 6.

      Object oriented programming

      5:24

    • 7.

      Dynamic colors with HSL

      1:52

    • 8.

      Randomized line art

      3:26

    • 9.

      Drawing multi-segmented lines

      3:25

    • 10.

      Animating lines

      4:36

    • 11.

      Rainbow lightning storm effect

      5:29

    • 12.

      Linear gradients

      2:24

    • 13.

      Radial gradients

      1:14

    • 14.

      How to fill shapes with a pattern

      4:03

    • 15.

      Improve your effects with shadows

      1:42

    • 16.

      Trigonometry

      1:51

    • 17.

      Chaos scribbles effect

      4:43

    • 18.

      What is a flow field

      1:07

    • 19.

      HTML canvas setup

      1:28

    • 20.

      Drawing shapes on canvas

      2:52

    • 21.

      Object oriented programming in JavaScript

      8:07

    • 22.

      Drawing particle systems

      1:17

    • 23.

      Animating particle systems

      3:54

    • 24.

      Drawing lines and trails

      3:46

    • 25.

      Motion patterns with trigonometry

      1:16

    • 26.

      Creating a vector field

      8:03

    • 27.

      How to create a flow field

      7:04

    • 28.

      Flow field experiments

      3:28

    • 29.

      Grid and debug mode

      8:17

    • 30.

      Randomized colors

      2:54

    • 31.

      Responsive design

      5:15

    • 32.

      Experimenting with flow field patterns

      2:43

    • 33.

      Drawing text

      5:53

    • 34.

      Understanding pixel data

      5:13

    • 35.

      Handling pixel data

      3:42

    • 36.

      Converting colors into angles

      10:28

    • 37.

      Flow and gradients

      4:47

    • 38.

      Tips, tricks & experiments

      10:39

    • 39.

      Image flow fields explained

      8:27

    • 40.

      Color manipulation tricks

      10:22

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

174

Students

1

Project

About This Class

Making beautiful things with code can be easy if you take it step by step. Let’s explore creative coding and discover what can be done by combining different drawing and animation techniques.

Knowledge is power. That’s why we will use just basic programming principles—functions, arrays, and for-loops—and plain vanilla JavaScript, no frameworks and no libraries. I will explain every line of code as we create multiple animated projects. Being able to fully understand how the code works will allow us to gain complete control over the code, and we can make it do whatever we want it to.

Let me show you how to draw a simple line, animate it, and then turn it into more and more complex effects as we add more techniques to our creative coding toolkit, one by one.

Discover the secrets of creative coding today. The biggest secret is that it’s actually much easier than you think to create complex, beautiful animations. You just have to take it step by step. Everything makes sense if you break the process down into individual parts.

Lessons 1 - 16:

In the first beginner-friendly block, we will learn how to draw shapes on canvas and how to animate them, turning them into animated spirals, rainbow lightning bolts, and random scribbles.

Lessons 17 - 39:

If you already understand the basics of drawing on an HTML canvas, you can skip directly to Section 2, where I start the main course project from scratch. We will create an animated flow field, experiment with it, and make it bend around text and images.

Have fun!

Meet Your Teacher

Teacher Profile Image

Frank Dvorak

Creative Coding

Teacher

Hello, I'm Frank. I'm a front-end web developer, owner of Frank's Laboratory YouTube channel. Come explore creative coding, game development and generative art with me.

See full profile

Level: Beginner

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Introduction: Great and beautiful things with code can be easy if you take it step-by-step, everything makes sense if you break it into individual parts, let me show you how to take basic programming principles, functions, arrays, and for loops to create complex animated code basis. Once we fully understand the techniques, we will take it to the next level in the advanced section. Today, we will discover the tools, cool, experiment with code, and we will use that knowledge to design our own animations. Let's make art with code and learn programming at the same time. Have fun. 2. HTML & CSS setup: In this first section, I will create a separate beginner friendly project. We will learn how to animate lines and how to turn them into multiple different effects, such as lightening, spirals, waves, and so on. If you want, you can jump straight into the flow field section, which starts from less than 17. I included source code downloads during key points in each project. So it's easier for you to jump between the projects and experiment with code. If that's what you want to do, I will guide you through the entire process step-by-step, and I will explain every line of code, but some basic knowledge of HTML, CSS, and JavaScript is needed to get the maximum value. Here, I'm just setting up a basic generic web page with HTML canvas element on it. I linked to style.css and script.js files. And then I declare some CSS styles for canvas and create global reset rules to make sure that margins and paddings appear the same across different browsers. I recommend using a modern browser for this class, ideally Google Chrome. You can see I'm using this common absolute position, center and technique to make sure that Canvas is in the middle of the web page, vertically and horizontally. 3. JavaScript setup: Basic setup inside index.HTML and style CSS, we move into script.js. All of our logic will be written in here. I set up my HTML canvas element, our drawing board with the usual two lines of code. I created a custom variable I call e.g. canvas, and I pointed towards the canvas element we created in index.HTML using its ID. I gave it an ID of Canvas one. Then I create a context variable, CTX. I take that canvas variable I just created, and I call built-in getContext method on it. This method creates so-called draw in context, which is basically a JavaScript object with all built-in Canvas drawing methods and properties. We pass it an argument called contexts type. We want to work with two-dimensional rendering context. Today, canvas element has two independent sizes. Element size, the actual size of the HTML element, and drawing surface size. We wanted to make sure both of these sizes are set to the same values. I do that by not defining canvas size in CSS and just certain width and height attributes on Canvas to the values. I want like this. If you are also declaring canvas size in your CSS file, make sure it's set to the same values to avoid stretched or otherwise distorted drawings. If I console log c dx from line two, we can see this canvas rendering context object I talked about. Inside. We can see the default Canvas settings such as the current fill style font. We can see that the default stroke style is set to black here, e.g. we can also see all the built-in Canvas drawing methods. Today, we will focus on drawing and animated lines. So we will learn everything you need to know about begin path method that comes from here. We also have closed path method here, line to here, and move to method here. Let's use these methods so we fully understand what they do and how they work. 4. How to draw rectangles: We can draw many different shapes on Canvas, too basic shapes are circles and rectangles. To draw a rectangle is very simple. We can take contexts we defined on line two. We know it holds a reference to the object that contains all canvas drawing methods. From there we call built-in fill rectangle method. Fill rectangle expects coordinates from which we will start to draw in the rectangle, the top-left corner of the rectangle. X-coordinate, the distance of the top-left corner of the rectangle from the left edge of the canvas will be 100 pixels, y coordinate, the distance from the top will also be 100 pixels. The width of the rectangle will be 50 pixels and the height will be 150. I can change the width to 200 pixels, vertical y coordinate 250 pixels. I think you get the idea by default, the fill of the shapes drawn on Canvas is set to black. I can use the fillStyle property to override the default value and I can set it to something else. Let's try red. We can also call built-in stroke rectangle method, which will just outline the shape. By default, it will be black line with one pixel width. I can change that using line width property. Let's try ten pixels. And I can use a stroke style to change the color of all lines drawn on Canvas, e.g. blue. 5. How to draw lines: So these are the very basics of drawing shapes on Canvas. Today, we will focus on drawing lines. They work a little bit different than rectangles. We need to start by colon built-in begin path method. This method will start a new path and it will also automatically close all existing path if there are any. This is important because this way you can draw each line in a different color and without begin path, all your lines will automatically join into one massive shape, which is not what we want today. Right? Now, we just want to draw a simple line. We start by using built-in move to method. This method will create a new subpath for our intents and purposes. Let's think about move to methods as a way to set the starting x and y coordinates of our line. Then we will use line to methods which will create a straight line from the subpath, from the point defined in move to two, the coordinates we pass to line two. Let's think about it for now that line two methods will represent the ending x and y coordinates of our line. Move to end line two methods just define the path. They don't directly render anything to draw the path onto us, we have to use built-in stroke method. Now, we are drawing a line from coordinates 300, 302, coordinates 350, 400. We can change these values to change where our line starts and where it ends. I can delete the rectangle. Now, in this area, I wanted to define global settings. Line width will be ten pixels or 100. I move the coordinates. We have many other properties we can use here. You can see all of them if you console log CTX from line two as we did before, e.g. look what happens when I said Line Cap property to round. I set the stroke style to magenta, line width to ten. We can also chain multiple line two methods like this. Each new line two methods, we will define another point in the shape that will be connected with our subpath. And the dentist wrote, this is the main concept of animation we are doing today. We will have a line that starts somewhere. Then we will have many line two calls to the find points along that path. And similar to the classic game of snake, we will animate this by always adding a new line segment to the start and removing the last segment, Making it look like the line is crawling over the screen. I add a new line. I add one more. I remove the oldest segment, I add another new line. We will automate this and we will play with the coordinates we pass to these lines to create interest in movement patterns. Once you understand this concept, we will use it to build beautiful flow fields like this one in an advanced class. It might be easier than you think to make the lines flow around images or letters. 6. Object oriented programming: Now that we understand the concept, let's scale it up. I wanted to create a custom JavaScript class that will draw and animate lines for me, I wanted to animate many lines at the same time. So using a class is ideal here. Class constructor is a special method to create an instance of a class. When we call it with the new keyword later class constructor, we'll create one new blank JavaScript object, and it will set its properties and values based on the blueprint inside. So let's define that blueprint. I want you to start very simple and we will optimize the code after to make sure it's absolutely clear what's going on. Each line object will have starting x and y coordinates. So a custom property I call e.g. start x will be a random point between zero and width horizontally. Starting vertical coordinate of each line will be a random value between zero and kind of as height vertically. So when we combine these random x and y coordinates, it will give us a point somewhere within the canvas area. I want to do the same thing for ending x and y coordinates like this. So when a new line object is created, it gets randomly assigned start point and end point. Let's give it a custom draw method to draw a line between these two points. I call begin path first. Move to method to define the starting x and y coordinates of the line. Line two method to define ending x and y coordinates of the line. And we call stroke to actually render that path on Canvas. We have a class, our blueprint is ready. How do we actually use it to create one line object? I create a constant variable, I call line one. I set it equal to new line like this. The new keyword will look for a class with this name and it will trigger its class constructor. Class constructor, we'll create one new blank JavaScript object, and it will assign it values and properties based on the blueprint inside that object will be saved in disgust them line one variable. Since this object was created using the line class, it has access to this public draw method and I can call it like this line one dot draw, random starting and ending coordinates were calculated and a line was drawn perfect. Up on line seven, uncertain linewidth to ten pixels. But maybe I want each line created by the class to have a random width within a predefined range. I created this dot line width property, and it will be a random value, 1-16 pixels. I will wrap it in Math.floor so we don't get sub pixel values. It's a good habit to do that for everything we draw on Canvas whenever possible. I could have also done it for x and y coordinates. I take CTX from line to line width and I set it to this randomized line width value. Now, every time I refresh my browser window, align with randomized position and randomized line width will be drawn. We are doing something that's considered a bad practice here. I defined canvas variable on line one, and I'm using that same variable directly inside my class. I do the same with ctx variable. The code will work, but if you are writing object-oriented code, the goal is to keep your classes independent of their lexical environment, independent of the code that's outside the class itself. Right? Now, this class will only work if Canvas and CTX variables are defined up here. The right way to do this is to convert this outside variables into class properties. That way, there will be passed to the class constructor here when the class is created. And they can come from anywhere in our code base, making our class more self-contained and reusable. You often ask me about why I do this in the comments, so I hope this explains it. Line class constructor will expect cannabis as an argument. Inside. I convert that argument into a class property. I'm saying take canvas variable that was passed here and convert it into Canvas property on this instance of line class you are creating right now. Then we can use this dot Canvas from line 12 here, instead of directly pulling the variable from line one. To make all this work, I know that line class constructor expects us as an argument. So I take canvas variable from line one and I pass it as an argument here. When we create an instance of that class with the new keyword perfect, We are also directly pulling CTX variable from land into our class here. So our public draw method will expect context as an argument and it will be used here. Then we take CT x-variable from line two and we pass it to draw method down here. When we call it that, CTX will get converted into context here. So this is how you make classes more self-enclosed and less dependent on the surrounding cold outside that class. 7. Dynamic colors with HSL: We are defining stroke style up on line eight, but maybe I want the color of the lines to be randomized as well. In that case, I have to define stroke style inside the draw method here. I said it's to write it just to test it. If it's working. I wanted to assign a random color every time we create a new line. The easiest way to do that is by using the HSL color declaration on the web, we can define colors in many different ways. You have your standard RGB and hex color declarations that everyone is familiar with. We can also use HSL color declaration, hue, saturation, lightness. The first argument here, you will represent a degree on the color wheel 0-360. Saturation will be 100 per cent. We want full colors. Lightness will be 50 per cent. We want a color not affected by light or dark. Zero degrees on the color wheel is right, as you can see, 120 is green, 240 is blue, 360. We cycled back to red. Again. As we increase the view, we travel over all the colors in between. If we go any higher than 360, we just cycled from the beginning from the red color again. Hsl color declaration is structured in a way that makes it very easy to randomize colors or to dynamically cycle through the color spectrum. I create a custom class property. I call this dot view. It will be a random value 0-360, and I want only integers. So Math.floor, we will make stroke style dynamic by replacing hard-coded hue value with this randomized variable like this. And that's it. Every time I refresh the page, we randomly calculate a different color. 8. Randomized line art: As we said, JavaScript class is a blueprint. We can use it to create many similar objects. So why are we creating only one randomized line? Let's create many more. At this point, it's very easy. First I create a variable called lions array. This will be an array to hold all airline objects in one place. Now I can create a for-loop. That for loop will run e.g. ten times. Every time it runs, i take lines array and I call built-in array push method. Push method. We'll take whatever we pass to it as an argument and it will push it to the end of the array. I will pass it a new line. And online 11, I can see that line class constructor expects Canvas as an argument. So I pass it canvas variable from line one. I delete this code, and I console log lines array. As expected, it contains ten line objects created using our custom line class. I open one and I inspected to make sure all the properties have values. I want you to check that nothing is undefined, which would be a signal of a problem. All is good here. I can also create a number of lines, a variable, and I set it to 50. I use it here in the for-loop. In the console, I can see that lines array now contains 50 line objects with randomized positions, linewidth, and colors. How do we draw them? Since we are holding all align objects inside lines array, we can simply call built-in array for each method on it, for each method will call a provided callback function once for each array element. So inside, for each line object in the array, I want to call its associated draw method we defined on line 20. If you are wondering where this word object is coming from, it's just a random word I chose to use. We are basically a sign-in each object in the array, a variable name that will be used to refer to that object inside this for each method, that variable name can be anything as long as we keep referring to it that way everywhere inside this callback method, I can also call it line e.g. so for each line object inside lines array called its draw method. On line 20, I can see that draw method expects context as an argument. So I pass it CTX variable. We defined on line two. We are drawing 50 lines on Canvas. Each one has randomized starting and ending coordinates, randomized width, and color. You can refresh the page to generate a new set of random values and draw them on Canvas. Congratulations if this is your first creative coding project, this is a very simple form of procedurally generated art, so-called generative art. Generative art is a collaboration between a human and a machine. Rules and scope of this artwork are decided by us, the human. But some of the features, in this case colors and positions, are randomly decided by a computer. There are of course, much more advanced forms of generative art. And I made many classes where we explore that in different ways. Let's see where we can take this one. Let's animate it. 9. Drawing multi-segmented lines: For clarity, I will go back to one line so we can better see what's happening. As we said in the beginning, I wanted to have a line made out of multiple segments. We will be adding new segments and remove an old ones to make it look as if it's crawling around like the classic snake game. I need to make some changes in the line class to achieve that, we will need only one set of x and y coordinates, like this. These coordinates will update for every tick of animation loop and we will add and hold a certain number of positions in a history array that I define here. First, it will be a simple object with x and y property equal to this dot x and this dot y. We just randomized here. This node history is an object now, but it will be an array where each element in that array is an object like this with x and y coordinate. Move to method will define the starting x and y coordinate of our path. And it will be this dot history array index zero, which will be object like this. And we access its x-coordinate and also its y-coordinate. Let's say I will keep ten objects inside history array. Each one has its x and y position value. Here I want to cycle through however many positions we hold in history array, and I want to connect them with a line. So I create a for loop which will run as many times depending on the length of this dot history array from line 15. And each time it runs, it will take object, which will look like this at that index in the array, and it will pass its x and y coordinates to line two method. So we have moved to method to set the starting x and y position of the line. And then we cycle through all other positions and we connect them with a line. Then we call stroke to render that multi segmented path on Canvas. We don't have animation loop yet, so I will create another for loop that we'll just simply run three times and create three randomized x and y positions. I need to make sure the stone history up here is actually an array. At first, it will contain just one element with an index of zero. It will be this object that has x and y properties set to this dot x and y coordinates. Here I run a for-loop three times. Each time it runs, i create new random x and y position. I take distort history array and I push another object with these new x and y properties in their careful about the brackets here. Nice. Every time I refresh the browser window, we draw a new multi-segment line starting x and y coordinates. And for loop that runs 123 times each time creating a line segment, a subpath. I can make the for-loop run 30 times if I want to, which will create much more complex shape. I hope it's clear now how the concept of history array works. Now we take it one step further to animate it. 10. Animating lines: I create a custom function I call e.g. animate. Inside, we will draw a line. We will update it. And then we call built-in Request Animation Frame, which tells the browser we want to perform an animation. This will make the browser call a function we specify before the next repaint. In practice, this means I simply pass it the name of its parent function, animate runs, it does something and then request animation frame. We'll call animate again, creating an endless repeating animation loop. Request animation frame method fits on the window object, but it can be called directly like this. So here we define animate. I need to call it. To start the first animation loop. I put a console log inside that says animating, just to check the loop is actually running. Yes, I deleted. I want to put our draw code inside like this. This is just creating an endlessly grow in line. We can temporarily comment outline for the seven. If your computer struggles with this, we will fix it in a second. I want to delete old paint between every animation frames, so I call built-in clear rectangle method. I want to clear the entire canvas from coordinate zero-zero to canvas width, canvas height. Okay, I comment this out because we are adding 30 line segments over and over into history array here on line 20 for creating an endlessly grow in array, I create an update method on the line class for every animation frame, every time update method runs, I want to add one new segments to our line. I will delete all of this. I uncomment line 47. And for each tick of animation, I want you to call draw method on every object inside the lines array. Currently, we have just one object in there. I also wanted to call the update method we just defined. Now we can see line gets drawn segment by segment. It doesn't make sense to cycle through the same array twice here. So instead, I call draw and update in the same callback function. Careful about the brackets here. Is it to make a mistake and get an error? Nice. I don't want it to be drawing a path with an endlessly increasing amount of points. I want to have a certain max length. And when that is reached, we will remove the oldest segment and we add a new one. Let's say I want the path to be ten segments long. Inside update method. As I add a new segment, I check if the length of history array is more than max length. If it is more than ten, we will use built-in array shift method. Array shift method removes the first element from the array because we are using push method to add a new segment. Push method adds a new element to the end of the array. Shift method will remove the oldest segment for us from the beginning of the array. And here we reach the second step in our procedural line art. What do you think about this? It scales, but with rules, we can add as many lines as we want. They all have randomized width and color because of the logic we wrote in the beginning, we're just randomly picking points inside cannabis. What if we want some sense of continuous motion? We want the updated position of the next segment to have some relation to the position of the previous segment. This is getting us one step closer to an actual flow field. I hope you're getting some value. Let's explore motion and now I will think of our line as a particle. Its first segment is moving along the canvas in a certain speed and direction. The other segments follow. I give each line speed X, horizontal speed and speed. Why vertical speed? We will start from a random point somewhere within canvas area. And every time we call update method, we increase the current exposition by the value of speed x and vertical y position by the value of speed. Why? Insight animation loop I will uncomment, draw and update to see what we just did. Okay. I create 20 lines. Yes. This is what's happening. 11. Rainbow lightning storm effect: I'm going for lightening effect. I want a rainbow lightning storm. Every time we update horizontal position by speed x of ten pixels, we will, on top of that, add a random value 0-10 and 50. What about a random value between -25 and plus 25 horizontally and the same vertically interests. Then when I refresh my page, I get a set of very short lightning bolts. If I want them to go more from the top to bottom, maybe horizontal speed will be only plus u2 and vertical speed 15 or 46. I would like the animation to play out and then reset. I could e.g. check if all segments left the canvas area. Or I can give each line a timer. Let's say each line has a lifespan of maxlength from line 18 times ten, each line will move around for 100 animation frames. Inside update method, I will increase the timer by one for every animation frame. How do I do this? If timer is less than lifespan? We animated the line. I also need to define this dot timer in the class constructor up here. And I initially set it to zero. When the timer is more than lifespan, we are still drawing the lines, but we're no longer adding and removing segments, so it looks like they froze in space. At that point, I want to start removing segments one by one until the line disappears. So else, and here I will again call shift on the history array. Bear with me a second. I need to work out the logic here. We need to make sure that there is always at least one element in the array. Else if this dot history dot length is less or equal than one, called reset method. We will define reset method. Now, will this work? Timer is increasing as long as the timer is less than than lifespan, add and remove positions, making the line crawl around. If the length is less or equal to one, reset it. Else, meaning the timer is more than lifespan, but length is not yet less or equal to one. Keep removing one segment at a time. I think this should work. Now I need to define the reset method that we'll just set the line back to its initial state so that we can animate it. Again. We will need to reset x and y position, and we will add that starting position inside history array. We will also reset timer back to zero so that it can count again. I'm getting a console error. Probably you notice this before I need to call a shift to remove the first segment from the array on the actual history array. Like this. Yes, this is what I wanted. Lines move around 400 animation frames and then they reset. They all reset at the same time because we hard-coded maxlength to be the same value for all of them. I set it to a random value, 10-160 segments. Now we're getting some real sense of motion. I can also randomize speed x, let's say a random value between -2.5 and plus 2.5 speed, y will be six. Speed. Why ten? Maybe here I should do random value between -0.5 and plus 0.5. Speed Y5 seven. I can also play with the random range here and here. Generating random numbers like this for each animation frame is very performance expensive, just so you know. But since we don't have thousands of lines, most computers probably won't even notice reducing how many times your codebase calls mastered random pair animation frame will massively increase performance of your effects. I said lifespan to max length times three. You can see that we have a line that starts from one segment. It grows until it reaches max length, then it calls around to a maximum of three terms. It's maxlength. At that point, it no longer grows. It shrinks from the last segment from the end, and then it disappears and resets. This type of line motion is exactly what you need to create flow fields. Now, one way how to implement it. I create 100 lines. This is my version of procedurally generated rainbow lightning storm. If you are a beginner and you followed all the way here, you are now a creative corridor with a knowledge of HTML canvas. Congratulations, feel free to add this project, your online resume. 12. Linear gradients: Html canvas offers so many different built-in tools that allow us to modify our animations. We just built the rainbow lightning storm effect. The lines we are drawing on Canvas are just shapes. Let me show you four very different ways how you can enhance the fill and stroke of your shapes using gradients, images, and shadows. Html canvas offers two methods to draw gradients. We can use create linear gradient method. It's very simple to use. Let me show you. It creates a gradient along a line connecting two given set of coordinates. First two arguments, we pass to it our x and y coordinates of the start point. I want my gradient to start at coordinate zero-zero. And I wanted the end point to be at coordinates canvas width, height. We drew an invisible line from here to here. Gradient will be drawn along this line. To work with gradients, it helps to save it in a variable. I call it e.g. gradient one. Then I take that variable and I call built-in add color stop method on it. Here we define offset and color. So at offset zero in the beginning of the gradient, I wanted to start from pink. I copy this line a few times. Let's start at offset 0.2. Actually, 0.3 will be read. 0.4 is orange, 0.5 is the yellow. 0.6 will be green. 0.7, turquoise, 0.8 is violet. That's enough. I think I'd take this stroke style declaration and I put it here after we define the gradient. And I will set it to the variable that holds our newly defined linear gradient. Notice there are no quotes, just the variable name like this. I also need to comment out line 33 so that our gradient gets applied. As you can see, we are drawing a linear gradient from coordinate zero-zero to canvas width, canvas height. I create 200 lines. This is a very different version of rainbow lightning storm, isn't it? 13. Radial gradients: I copy this line of code. I will call it gradient to. We also have create a radial gradient method. This one expects at least six arguments, x and y of the center point of the inner start circle and its radius. I want the circle center point to be in the middle of Canvas. So Canvas width times 0.5, canvas height times 0.5, and radius will be 30 pixels. The end circle will be also starting in the middle of canvas with a radius of 200. I give it to color stops just to show you what that looks like. I think you already understand this concept anyway. At 0.4 offset, we will have green from 0.6, it will be blue. Then I set the stroke style to Gradient tool. Here. I can change the radius value. I can add color stop here. So this is how you use linear and radial gradient. 14. How to fill shapes with a pattern: As we are filling our shapes with gradients, we can fill them with a pattern. That pattern can be something we draw on an off-screen canvas, but we can also use an image. In index.HTML. I create an IMG element. I give it an ID of pattern image. As a source, I would normally use a path towards that image file, but that would give us problems if we run our code locally just from a folder without using a server to avoid any cross-origin resource sharing issues, I will turn the actual image into base 64 code, and I will make that code part of our index HTML file. When I do that, the image will be considered the same origin in all scenarios. So I don't have to deal with anything server site. In this course, I want to focus on front-end web development. I prepared an image that you can download in the resources section below, I made it the same size as our canvas. We can also use your own image if you want. I can use JavaScript to turn image into base-64 data string by drawing it on Canvas and using two data URL built-in method. But for simplicity, I will use a website to do that for me, there are many websites that can turn images into code in seconds for us, I just go to Google and I searched for PNG to base 64 phrase. I will choose this link online PNG tools.com. I drag and drop the image I want to convert here and it generates a data string for me. We have to make sure that the string starts with base 64. If it doesn't fall, you click this checkbox to get the correct format. I copy this entire code block. It's a lot of code depending on how big the image you are using. S. I paste it here inside the source argument, inside as RC. In Visual Studio Code, I can go to View where drop to put this extremely long line of code onto one line, making it easier to navigate in this code file. I go to style CSS. Actually I saved the changes in index.HTML and you can see that we are drawing the image on the web page without needing the actual image file. The code we just generated replaces the image file completely and it contains all the pixel information about that image. I hide the image with CSS using its ID display none. Okay, we have an image we can use. So in this area, we define the gradients. Here, we will create a pattern. It's also very easy. I will need a reference to that IMG element we just created in index.HTML. I create a variable I call pattern image, and I pointed JavaScript towards the IMG element using its ID. Another variable I call pattern one is equal to ctx dot. And here we use built-in create pattern method. This method works very similar to the two methods we used to create gradients. It takes two arguments, image and repetition, and we can then use it as fillStyle or stroke style to fill our shapes with that image. Let me show you what that looks like. Image to draw will be pattern image we just created. And repetition rule will be no repeat. Now I simply set a stroke style to button one. And here we go. We can see that our lines are now filled with an image. This technique can be used as a way to reveal an image, e.g. depending on what kind of application or effect you are building, we can also use a seamless tile as an image to create a repeating pattern, which is probably what this method was designed to do. As the main purpose, I can use wider lines to see more of the image e.g. 15. Improve your effects with shadows: I like to use Canvas shadows to get my shapes a subtle 3D effect. I said shadow offset to two pixels. This will define the distance between shadow and the shape horizontally. Shadow offset y, vertical distance will also be two pixels. This can be positive or negative values, by the way, depending on which side, relative to the shape you want the shadow to be. Shadow color will be white. This will give us this interesting cracks affect or reduce the number of lines. Shadow color block. I can go back to gradient one, the linear gradient. Now we can better see what the shadow is doing and how it highlights the edges. If you have any performance issues and FPS drops, the quickest way to fix it is to reduce the number of lines you are animating. And you can also disable these shadows. I supply and width to a smaller value. Our rainbow lightning storm can take many forms, and I think you have plenty of tools now to adjust it and make it your own. Let's do something completely different with this. Now. How about instead of lightning, I want spirals. Let's talk about trigonometry. As always, you might want to create a copy of your code to save this effect for a future reference, we will make radical changes to the code now and we will transform it to a completely different generative art piece. 16. Trigonometry: Trigonometry is all about angles. I want each line object to have its own angle property. So I define it here. For every animation frame, I will be endlessly increasing that angle value by 0.1. This value will represent radians. So instead of doing this lightening effect here, I will increase x by Masdar sign and I pass it this ever-increasing angle value. This will create left and right horizontal motion like this. Parsing and every increase in value to methoxide will just map these values along a sine wave. What if I want the waves to go further left and right? I create a property called this dotted curve and I set it to 30. By default, method sine is cycling between minus one and plus one. If I multiply that value by this dotted curve, we increase that range. And this is the result. I want smooth lines, so I remove this part of code here. Try to use different value for the curve. If you need more clarity on how this value affects the motion of our lines. What if I wanted to curve to start at a very small value and gradually increase, starting curve will be 0.1. And Vc, the speed at which it will be increasingly will be 0.25. Inside update method for every animation frame, I wanted to increase curve by VC like this. And when lines restart, I want the angle and the curve to go back to zero. Interesting effect. We can do so many different things with it now. 17. Chaos scribbles effect: What if I also increased vertical y coordinate by sine of the angle times curve, we get this strange back-and-forth motion. But if I use cosine instead of sine and cosine, we'll work together to map a path along a radius of a circle. And because curve is increasing as animation runs, we don't get Circle. We actually get spirals. I said VC, velocity of curve to a much smaller value. Now we are drawing spirals like this. I can also make it a random value between -0.2 and plus 0.2 minus values will move in the opposite direction. Maybe I also want to randomize how fast the angle increases. Va, Velocity of angle will be a random value between -0.25 and plus 0.25. Then I use it here instead of the hard-coded value. The speed at which the angle is increasing will affect how compact the curve is, how fast it spreads out. This is symmetry. I want to add some chaos now for this simple art project, or imagine someone is drawing these beautiful symmetrical spirals. But at a certain point they get angry and they start scribbling randomly and they break the spiral shape. Let's say if the timer is more than half of the lifespan, the second half of lines life cycle start multiplying va by minus one, switching it to the opposite value each time a new animation frame takes, each time a new segment is drawn. This will break the spiral shaped like this. We have a path that starts as a spiral and then it turns into a broken line. I wanted the line to be more and more broken. We need more chaos at the end. Yes. Let's change this to 0.9. Most of the time it will be symmetry. Last 10% will be chaos because we are modifying va value and we are not bringing it back to its original range. Lines stop making spirals the way they should. As the lines reset, we need to be also reset and va value. I copy line 48 and I paste it down here inside the reset method. Nice. We started with symmetry and we end in chaos. Exactly like me on a Saturday night. We are constantly calculating lifespan times 0.9 because this calculation happens so often to make it easier for JavaScript to run this code and help the performance a little bit. We can precalculate this just once in the class constructor, and we will then use that pre calculated value here. I will call it e.g. this dot breakpoint and it will be lifespan times 0.85. Then I can use this dot breakpoint here. And instead of calculating this 60 times per second for every line object, now, we are just calculating it once for each line at the point where each line object gets created using the class constructor. I make the lines wider. I said canvas background to black. Yes, this is the effect I wanted. I comment out stroke style on line 54. Now the stroke is black so we don't see anything. I can give it to white color. Now we can see again how the shadows give it a very subtle 3D effect. I said number of lines, 250. Be careful here. It might be too much for older computers and you might get lower FPS. I do 100, I'm just experimenting now. I can set the stroke style to linear gradient we defined on line 15 just to see what that looks like. How about I use radial gradient? For this one, I should definitely choose different colors to make it look nice. I can also use the image pattern here again. Interesting. Have you seen this great pattern method being used before or is this your first time? Now you have all the tools you need to create fully animated, beautiful flow fields, check out the links in the description and join me in an advanced class where we use the techniques we learned today to create more generative art. 18. What is a flow field: Here is a grid of angles. We can also call it a vector field if we add particle system on top of it and make the particles flow over the grid. Their motion is influenced by the field vector. The direction and speed of movement will be influenced by the angle value stored in the cell each particle is currently moving over. This class is for creative people who wants to understand how the code works under the hood. Don't forget to check the resources section to download source code if you want to compare it with mine. I included multiple source code downloads at key points in the project as we progress with the class in case you want to inspect my code and compare it with yours. I created a generic web page with HTML canvas element on it. I linked to style.css and script.js files. I did a global CSS reset and I gave canvas element a black background. 19. HTML canvas setup: After a simple HTML and CSS set up, everything else in the project will be handled by JavaScript here inside script.js file. As always, I need to set up my Canvas custom variable called cannabis will be document, get element by ID and ID I gave it was canvas one. Ctx context is that canvas variable dot getContext. And I pass it to the to create an instance of canvas rendering context object. Now, the CTX contains all built-in to the drawing methods and properties that any web browser can understand. I want you to have a full-screen effect today. So width will be window in a rave and canvas height is window in her height. I'm not sure if you can see it, but doing this gave my web page scroll bars. I don't want them so easy fix is to give canvas element position absolute, like this, nice. So this entire black area is our Canvas, our drawing board. I can use ctx variable from line tool to draw something. Now, if I console gate, we can see the default settings. E.g. I. Can see that the default kind of as fillStyle is black to make sure the shapes we draw our visible against the black background. I said fillStyle to white. 20. Drawing shapes on canvas: Down here we can see all built-in Canvas drawing methods, e.g. the arc method, which can be used to create an arc or a full circle. If we had more shapes on Canvas, I will have to call begin path. But now I can skip it. And let's say I want a circle with a center point at coordinates 100 horizontally from the left edge of Canvas, 100 vertically from the top edge. Radius of the circle will be 50 pixels. Start angle of the arc will be 0 rad. And since I want to draw a full circle, the end angle will be math.pi times two, which is approximately 6.28 rad or 360 degrees. Full circle. Arc method works with a path, so it doesn't directly render anything on cannabis. It just positioned an invisible path. We can choose to stroke that path or to fill that shape with color. So I call built-in fill method like this. I can move the central point of the circle around. I can change the radius. I can even change the end angle or start angle to get a different shape. Okay, so our Canvas is set up and we tested it by drawing a shape. In this area, I will keep my global Canvas settings to keep our code organized. Today, we are building animated flow field. It will be made out of individual lines. So how do we draw a simple line on Canvas? We start by Colin, built-in and begin path method. This will start a new path as well as clothes all previous paths. If there are any, we call moved to method to set the starting x and y coordinates of the line. We call a line to method to set the end in coordinates. Or we can chain multiple line to, to create a more complex shape, which is what we will be doing today. Same as the arc method. Move to end line two methods just said above. They don't directly render anything on Canvas. Now we have to choose if we want to stroke the path or if we want to fill that shape with color, I will call built-in stroke method to draw the path. Default stroke style is black, so it's not visible against the black background. I go up here and I said stroke style to white. Now we can see we are drawing a line from coordinates 100, 202, coordinates 400, 500. By default, the line is one pixel wide. I can use line width property to set it to ten pixels or 100. I can also use line gap and set it to round e.g. 21. Object oriented programming in JavaScript: Okay, So this is how we draw and style lines on HTML canvas. This is all we need to know to create a wide variety of effects. I wanted to create many animated lines that will flow around our Canvas. I will use the JavaScript class called particle. Like this. Each particle will be moving around and we will connect its positions with a line. Javascript classes are blueprints to create many similar objects. And we are about to build a so-called particle system made out of many particles. I will also have a class called effect. This could have been just a simple object because we will only have one instance of this class. It will be the main brain of our code base. So we have particle class, a blueprint we will use whenever we need to create a new particle object. And we will have affect class that will manage the entire effect, all the particles at once. I'm using classes and object-oriented programming to keep our code clean, well organized, and easy to read. This is a tutorial for beginners, so I want everything to be cleaned and as easy to understand as possible. It's my main goal. Affect class will need to be aware of available cannabis space. So constructor will expect width and height to come as arguments from the outside when an instance of this class is created using the new keyword inside, I converted these arguments into class properties. Take with passed as an argument here and convert it to width property on this instance of affect class, same for height. Affect class will have initialized method. This method will run just once to set everything up. We will have this dog particles property. This will be an array that holds all currently active particle objects. So when we initialize this effect, we want to take this load particles array and we want to use built-in array push method to push one new particle into the array. That particle will be created using the class we defined on line 11. So let's write that particle class right now. Every particle will also need to be aware of canvas width and height because it needs to know when it moves outside of the canvas area to keep our code modular instead of boolean canvas size from lines 3.4, I will take that value from the main effect class. I wanted to access these properties from lines 920. I will give particle class access to the entire effect class. Particle class constructor will expect effect as an argument and inside we convert it into a class property. So here we are pointing towards the main effect object. Keep in mind that objects in JavaScript are so-called to reference the datatypes. We are not creating a copy of effect object. Every time we create a new particle, we are just pointing to that same space in memory that holds the effect class from all the particle objects. That way particles have access to all methods and properties on the main effect glass. And also if any of these properties update because we are just pointing to that object, any changes on the effect object will be immediately visible from the particle objects, which will be very useful for us today. As I said before, we will have one effect object to manage everything. And we will have many particles, hundreds or maybe thousands. Let's see how it goes. Each particle will have starting x and y coordinate. Horizontal x coordinate will be a random value between zero and this dot effect from line 13 dot width, we are accessing this property from line 20. Vertical y coordinate will be a random value between zero and this dot effect dot height. To avoid sub pixel values, it's a good practice to use integers, whole numbers without decimal points by wrapping these in Math.floor, this will round the value down to the nearest lower integer. Each particle will have a public draw method. This method will expect contexts as an argument to specify which kind of us we wanted to draw on. I pass it as an argument like this to keep our code more modular and less dependent on the outside environment. Then we take that context and we call built-in fill rectangle method. Fill rectangle method is very simple. It will just take these randomize X and Y coordinates we just defined, and it will draw a rectangle there. Its width and height will be e.g. ten times ten pixels. We have our particle class and we have affect class. I wanted to create an instance of effect class. I create a custom variable I call e.g. effect, and it will be equal to new effect like this. The new keyword will look for class with that name. It finds it up here and it will trigger its class constructor online 23. We can see that effect expects width and height as arguments. So we pass it canvas width from line three. And kind of as height from line four, like this. Now, I can take that affect the variable and call init method we defined on line 28. Init method will take particles array and it will push one new particle object into the array. We get an error because here on line 12, you can see particle class constructor expects a reference to the main effect class as an argument, then uses that reference to access width and height properties of Canvas. Since we are creating that particle inside the effect class itself, I pass it this keyword, which in this area refers to the entire effect class. Nice. So after we created an instance of effect glass, and after we call this init method, I expect that particles array from line 26 contains one particle object. I console log the effect glass to check. I can see the effect object. It has width and height properties. And if I open this particles array, I can see there is one particle object inside. I check if all properties have values. If I see undefined or non somewhere, it would indicate a problem. All is good here. Now, I need a way to actually draw my effect on Canvas. I will give it another method I call e.g. render inside our cycle over all particle objects inside particles array using array for each method. For now we know there is only one particle object inside. For each particle object inside particles array, I call its associated withdrawal method. We defined that draw method on line 17 and we can see it expects context as an argument. So I take render method and I call it we need this context. So I take CTX from line two. I pass it to render. Inside render it will be expected as context. And we pass that context reference along to draw method because we know it's expected on line 17. From there, fill rectangle method is called and our particle is drawn. Nice. I can change the width and height when I refresh the browser window, x and y coordinates from lines 14.15 get randomized and particle is drawn. 22. Drawing particle systems: The main effect class I create a property I call number of particles. Initialized method will run, just wants to set our effect up inside. I will create these 50 particles. I can do that by using a for loop. It will run 50 times, and every time it runs, it will take particles array, and it will push one new particle with randomized x and y coordinates inside. And this is how you create a simple particle system. If you want, you can save this code as a boilerplate in a different folder because you can use it for so many different experiments, affects, and generative art pieces. I'm calling init method from here on the first page load. I can also remove this code and I call this.in it from inside the effect class constructor. Because when we create an instance of a class using the new keyword constructor will run all the code in the blueprint line by line. We can take advantage of that behavior by putting any code in here that we want to run at the same point when an instance of this class is created. 23. Animating particle systems: We are building a flow field. I want these particles to move around and leave trails lines behind them. Let's animate this speed X horizontal speed will be one. Particles will move one pixel per animation frame in the positive direction. On the horizontal x-axis, they will move to the right. Speed. Y will be one pixel per frame as well. Positive direction on the vertical y-axis means down. Since both of these forces will be applied to the particle at the same time, I expected the particles to be moving towards the bottom right corner of Canvas. Let's see. Motion will be handled inside update method. This method will define a single-step in the animation. And when we call this update method 62 times per second, it will create an illusion of motion. For every animation frame, I want to increase horizontal x position of this particle from line 14 by speed x value from line 16. I also want to increase the vertical position by speed. Why? Here inside render, as we cycle through all particles, we trigger there draw method and we also call the update method we just wrote. This gets called, just wants so there is no motion. And he's to call this render method over and over. I wanted to draw particles, update their positions, draw them again at those updated positions, update them again, draw them again, and so on. Calling this over and over, we'll create animation, an illusion of movement. We need animation loop. I create a custom function I call e.g. animate from inside our call render to draw and update all particles. And then I call built-in Request Animation Frame and method. I pass it, animate the name of its parent function. So animate runs, render is called particles are drawn and updated and then request animation frame and method calls animate. Again, we have a loop request animation frame method was designed for this purpose, and it has special features such as it all to generate the timestamps for us. And it also adjust itself to screen refresh rate. I have a regular screen, so my animations run at 60 frames per second. If you have a high refresh rate game and screen, your animations will run faster. You can easily use request animation frame and it's auto-generated timestamp to set any animation speed you want. Now everything is ready and I can call animate our expected all particles to move one pixel to the right and one pixel down there animation frame. They are leaving trails because we can see old paint, their previous positions. If I wanted to see only the current animation frame, I need to delete Canvas between each frame using clear rectangle method. I want to clear the entire canvas from coordinates zero-zero to canvas width, canvas height. Nice. We are animating. I change speeds. The relationship between speed x and speed. Why? We'll define the direction and speed of motion. We can also randomize the speed of each particle. How about a random value between -2.5 and plus 2.5 minus value will move to the left, plus values to the right. On the vertical axis, negative values will move particles up. Positive values will move particles down. The ratio between randomized speed x and speed. Why will determine the final direction of movement? And now particles move in all possible directions. I can make the particles smaller. 24. Drawing lines and trails: I actually want to draw lines. I call Big Bath method to tell JavaScript I wanted to start a new path. As the particle moves around Canvas for each animation frame, it will update its x and y position. And I will keep track of those positions as simple objects with x and y properties inside the history array. At first, each particle will be assigned a random x and y position somewhere within canvas area. And we use these values as the first object, the first element with an index of zero inside the history array. The start point of the line will be saved by move to method. We want to start from x and y coordinates of the first object inside the history array object with an index of zero. Then we create a for loop. It will run as long as there are any more elements in the history array. For every object, all these objects look like this. For each position object inside history array, we will take x and y coordinates of an object at that index. As the for-loop cycles over the array, we pass its x and y coordinates to line two methods. So start point of the line. Then we run through all positions inside history array and we connect them with a line. Then we stroke that path to actually draw it on Canvas. Now I need to make sure positions are being added to the history array as the particle travels around. Whenever we update particle position, we take this dot history array and we push an object like this with these updated x and y values inside. Nice, It looks like it's just one straight line, but it's actually a shape made out of many smaller line segments. I can e.g. make it wiggle by adding a random value between -1.5 and plus 1.5. This should make it more visually clear that these lines are made out of segments. I can do the same thing vertically. I can increase the random range may be from -2.5 to plus 2.5. I can increase the range even more. A random value between -7.5 and plus 7.5. Finally, this is starting to look interesting. Right? Now. These are just endlessly grow in lines. What if I want to define max length of a line? In this case, this will mean max number of segments. Inside the update method. I say if the length of history array is more than maxlength, call built-in array shift method. So we have two array methods here. Push method adds a new element to the end of an array. Shift method removes one element from the beginning. Because we are adding new elements to the end of the array. Shift method removes the oldest segment. So airlines crawl around like this. They can look like small creatures under microscope. I could just give them some AI and make this into a game. I can increase max length of the line to 100s segments. Or I can do a random value 10-110. Now, some of them have short and some have very long tails. 25. Motion patterns with trigonometry: I can also apply trigonometry, which particle will have angled property which will initially start at zero. For every animation frame, I will increase angle by some small value. Angle value is in radians. Then I can pass that ever increasing angle to Martha sign, which will automatically map these values along a sine wave. This will give the horizontal coordinate a irregular wave emotion. Look what happens when I do this for the vertical position as well. Instead of adding here, I try and multiplication. I do it for both combination of sine and cosine. We'll map the positions along the radius of a circle, multiplying it by a value like this will increase the radius. You can try to use different combinations of values and apply different operations if you want to experiment, there are many things that can be done here. Basically, we built a particle system where each particle draws a line behind it. I comment out a line 69 for a while. 26. Creating a vector field: Now my goal is to make these lines travel around the canvas following a specific order to create a flow field effect. Flow field is based around a grid of angles. Each cell in the grid stores and angle value. And each cell in this grid in the flow field has a relationship with its neighboring cells. They are not random. They increase or decrease gradually. And as the particle travels over the grid, cell by cell, particle speed and direction of movement is influenced by this angle values, creating a motion pattern. Very common algorithm to generate angle values for a flow field is Perlin noise. It creates a gradient noise that gradually changes values in a random way. The resulting effect looks very natural and can be used to generate textures for procedurally generated serine or many other things. Today we will generate a spiral flow field where we use sine and cosine values and we attach them to vertical and horizontal coordinate of each point of each cell. This creates a beautiful symmetrical repeating pattern. We will be able to zoom in and zoom out of the pattern for very different and results. Let's take it step-by-step and it will become clear what I'm talking about as we build it. Our goal now is to create a grid that splits the entire canvas into individual cells. Let's say I want to sell size each cell to be 20 times 20 pixels. This will split the canvas into rows and columns. The number of rows and columns will depend on the width and height of Canvas and on this cell size. I could calculate the number of rows and columns here. But because I want this effect to be responsive, I will calculate that inside init method. We can then call that init method every time resize event happens to correctly recalculate the entire flow field grid. This load flow field will be an array that holds angle values for individual cells in the grid. The number of rows will be the height of canvas area divided by cell size. The number of columns will be wave divided by cell size. We will also set flow field to an empty array in case this init method is called while resizing Canvas. That way, all the old values will be deleted and we can recalculate the new values. Now, I want to travel over that grid whenever there is a great involved, the easiest way to handle it is with two nested for loops, the outer for loop, the cycle over the grid, row by row from row zero, as long as there are any more rows. The inner for loop will travel over each row from left to right. So we enter the outer loop, we are on the row zero. We enter the inner loop and we cycled through the cells on that row one-by-one. We reach the end, we exited the inner loop. We enter the outer loop, Y increases, and we enter the next row. Again. We cycled through all the positions on that row one-by-one. We repeat this process row by row until we cover the entire Canvas. Each time we enter a new cell, we create an angle value. We can do different things here. I will simply tie x and y coordinate to sine and cosine to create a spiral pattern. We know that if we pass an increase in angle value to sine or cosine, these methods will map this increase in values along a wave. In this case, those increasing values are x and y positions of the cells in the grid. So as we move along the grid, we create spirals and curves. Every time we generate a new angle value, we push it into flow field array. Keep in mind that we are cycling, integrate row by row, generating these values, but we are storing them in an array as a single continuous sequence of values. We need to keep this in mind when we need to extract this angle values again, to correctly match them to particles current x and y position. So at this point, flow field array should be filled with angle values. So let's console log it. I'm actually console log in it after every row, I should have put the console log here after it's all done, but it doesn't matter. We can see that angle values are being generated. Perfect. So we cycled over the canvas, integrate, and for each cell in that grid, we generated angle value and we start all these angle values inside flow field array. Because we passed this gradual increase in x and y values to sine and cosine, we created beautiful symmetrical pattern. The pattern will be revealed when we draw the flow field. So let's do it. We already have particles that move over Canvas and they drag lines behind them. Currently, we are using this logic to move them. Let's move all this away and completely changed this code in the update method here I want particles to move around and the direction of their movement will depend on the position of the particle as it travels over different cells in the flow field. The angle values in the flow field change cell by cell. And this angle will determine the motion of the particle that's currently flowing over that cell. There are many different ways to do this. I will create a helper temporary x variable, and it will be equal to particles current exposition divided by cell size. Basically, I'm saying how many cells in our grid from the left edge of Canvas, this particle currently is, in which column we currently are. Remember that cell size was set to 20. I want integers here, so I will round the value down. Helper y-variable will be particles current y position divided by cell size. How many cells? From the top we are on which row. Now I can use these values to map x and y coordinates of a particle to an index in the flow field array. Basically I have a particle with certain x and y position in a two-dimensional grid. And I have an array which represents that grid, but it's just one long set of numbers. So what I'm trying to do now is to map that x and y position to that index so I can extract the correct angle value. The grid nanoparticles current vertical and horizontal position. And I know over which column and row in the flow field grid it is currently moving. Now I need to use this column and row value to get the angle value of the cell from flow field array. This is a bit complicated to visualize if you are a beginner, so don't worry about it too much to get index of disposition from fluid-filled array, I take the row number we calculated here, and I multiply it by the number of columns that calculates this block rows, terms, cells per row basically needs to know which cell we are currently in because we added one angle value per cell into flow field array. Then we add however many cells we are into the current row in the grid like this. This gives us index inside flow field array that holds angle value of the cell we are currently moving over. Then we set angle value of this particle to the index inside flow field array we just calculated. 27. How to create a flow field: Now we have multiple different options how to use it to achieve different patterns. I will set speed x two cosine of the angle at first. Speed y will be the sign of the same value we just extracted online 35. Then I take particle screened horizontal position, and I increase it by that new speed value. And I do the same for the vertical position, increasing it by vertical speed. I delete this code. So we sliced us into an invisible grid. Each cell is 20 times 20 pixels and it has a specific angle value. We calculate which column and which row of this grid the particle is currently moving over. We map that column and row value to index in flow field array to extract angle value that belongs to the cell we need, we increase speed x by cosine of that angle speed. Why by sign? This will give us a direction of movement depending on that angle. Then we increased particles X and Y position. By these speeds. We push this new particle position inside history arrays so that we can animate its tail, the line that each particle drugs alone. If length of the line is more than maxlength, we remove the oldest segment. I uncommented render to see what motion we get. It doesn't look like it, but we already have a working flow field with sine and cosine spiral pattern. We are just zoomed out really far away from it. It will make more sense as we play with the code now. So the base idea is that parsing an ever increasing angle value to sine and cosine, we'll make the values cycle periodically between minus one and plus one. We use X and Y position of each particle as these increase in angle values. So the pattern will be tied to coordinates in that way. I close this in brackets and I multiply it times two. This will increase the radius of the curve. I remove the rectangle. I only wanted lines. I make max length of the line to be a random value, 10-210 segments. I can use different values here. Let's increase the number of particles so we can better see the pattern. Look what happens if I slowly increase this value, 0.5. I can go much higher than this. I think around ten. Let's put this value into variable, into a class property. I call this the dotted curve. I will show you why I call it curve in a minute. It will be clear. I use that property here. Okay? I can also multiply x and y by a value. If the values the same, we will get a symmetrical zoom into that pattern. It's not yet fully obvious, but a clear pattern will emerge from this. Bear with me. I want these values to be identical for symmetry, and I will put them into a class property. I call this Zoom. I said this the Zoom to zero point. To keep in mind that curve Zoom is also connected to cell size because of how we tied the sine and cosine curve to a position in a great change in cell size will affect the pattern. It will affect the Zoom. I think at this point we can already call it a flow field. Slowly increase the zoom to show you how that value affects the pattern. I can go really into the pattern, zoom in close and slowly, I zoom out of it. Still. I don't think it's obvious what the pattern is. Let's go up here to particle class constructor. I can remove these initial values of speed x and speed. Why? Because they get immediately overwritten here inside of the update method. I want each line to move at a different speed. So I will give each particle a speed modifier property, and it will be a random integer 1-3. Down here, I multiply speed x and speed. Why by that speed modifier. Now some lines move slower or faster. Let's increase the speed. Modify arrange. To show you clearly how curve and zoom effect the pattern. We need lines to reset when they finish their animation sequence. I create a method I call e.g. reset. When the reset I said x and y coordinate to a new random position somewhere around the canvas. And I also need to set history array to these new values as the initial position with an index of zero. Right now, the largest moves endless list somewhere far away off screen. I will create a property I call e.g. timer. It will be max length of the line times two. For every tick of animation, I will decrease the timer by one. Timer starts as a value equal to maxlength. Terms to it decreases by one for every animation frame. As long as the timer is more or equal to one, we will animate the line and make it crawl around like a dust. Already. When timer reaches one, the lines will freeze in place like this. At this point, we want an else statement to run which will start to remove an old segments from each line one-by-one, making the line disappear from the end. I do that by calling shift on the history array. This will give us an error when we remove everything from the history array, but we still try to draw it. So I will only run this else statement. If history array contains more than one element, only then remove all the segments from the line. Lines flow around. When the timer reaches zero, we start removing all segments until there are no more segments left. Perfect. At that point, I want the line to reset and repeat this animation cycle again. So else call this dot reset method we just defined on line 56. Okay, it's not working because I also need to reset the timer back to its original value. Congratulations, you build a flow field. This is a very solid code base and we can do so many things with it. It's finally time to properly use Curve and zoom to show you the pattern I was talking about. 28. Flow field experiments: As we said, Berlin noise will create a natural look in gradient noise pattern. We are calculating angle values for our flow field online 85 using trigonometry. So even though it doesn't look like it, now, this is a very symmetrical mathematical pattern that repeats endlessly in every direction. Let me show you what I mean. Look what happens when I increase the value of curve. I really enjoy watching this effect. There is something very relaxed and about this, I think now you can see a little bit that we have a repeating pattern. I change the zoom and I zoom away from it to clearly see the repetition. I can also zoom really close into the pattern and we just look at the small part of it. Kinda looks like the flow is actually random, but we know it's not. I'll increase the number of particles. My computer can handle this, but you should adjust the number of particles to make sure your computer can animate this smoothly so you don't have to use 2000 particles here. Try different values and see. I don't recommend going much over 2000 unless you have a very powerful computer. Flow fields are such a beautiful effect. And now you know how to build one yourself. We're just playing with sine and cosine pattern here, but we can use this code to change the flow and make it follow many different patterns. Look what happens when I change the curve of value. As the value gets higher, a spiral pattern will start emerging. That knee go a bit higher. It will start curving in a spiral here. And as I increase the value, it spirals more and more. It will kind of create a spiral snail shape if we go high enough. Now it's becoming more obvious. And even more, the curve just spirals in on itself. Interesting, isn't it? So now I think it's very obvious how the curve affects the spiral shape of the flow field. This pattern is endlessly repeating in every direction and we can zoom in and out of it by changing the values we use as this dot zoom on line 75, I can zoom really close into the pattern. And as I increase this value, we are slowly zooming out. At this point we can see the repeated nature of this pattern. As we assumed far enough. I go even further. I can make it spiral more. You see how the pattern is jagged and not very smooth. This is exposing the grid-like nature of the flow field as the angle only changes every 20 pixels, I can reduce the cell size to make the flow more smoothly. And basically slicing cannabis into smaller pieces, five times five pixels. As we can fit more columns now into the available canvas area, change in cell size also zoomed really far away from the pattern. Now we can clearly see how it's endlessly repeating. It's not random like Perlin noise. It's very mathematical and symmetrical. I zoom in a bit further. I think it's clear how this works. Now, feel free to play with it and do your own experiments. 29. Grid and debug mode: Both node is basically a grid of angles. Let's visualize that grid to make it clear how exactly it works. Let's create a debug mode on the main effect class, I create a method I call e.g. draw a grid. It will expect contexts as an argument inside I create a for-loop. First, let's draw vertical lines, columns. This dot calls property already holds a value representing the number of columns in the grid. So we use it here. Every time we enter a new column, we call begin path. I wanted to draw a vertical line. Move to method to define the starting x and y coordinates of the line. Each line will start at dynamic horizontal position, cell size, the width of a single cell times the number of the column we are currently in. All vertical lines will start at the top from y coordinate zero. Line two will determine the ending x and y coordinates of our grid lines. Horizontal position will again be cell size times column number. And I want the lines to end all the way at the bottom of the canvas area. So this dot height. Then we stroked lines. Let's see what we have so far by Colin draw grid method from inside the render method. Nice. We see vertical lines representing columns in our flow field. Cell size is only five, so we see line every five pixels, I change it to a 30. As you can see, this also affected the zoom into the pattern. As the cell size increased, we can fit less cells per row, meaning that the spirals created by sine and cosine online 85 have less time to develop. It's all because we tight angle value, we pass to sine and cosine to column and row numbers, less columns, lower range of values passed as angle to sine and cosine online 85, which affects the pattern. I can always make up for that by adjusting this zoom property on line 75. Okay, we are drawing vertical lines. Let's also draw horizontal lines, the rows, to complete our grid. Again, we start by begin path starting x and y coordinates of each horizontal line will be zero horizontally. So from the left edge of Canvas and vertically it will be cell size times the row number. Endpoint of each line will be the right edge of Canvas. So this dot width horizontally, vertical endpoint will be again cell size times row number. I stroke the lines. Perfect. Now we can see the full grid. Look closely at the lines. Do you see how the angle value, the direction of the line changes at the points as lines enter new cells in the grid. Each cell in the flow field has different angle value assigned to it, and lines follow those angles to give them direction of movement. This visual should help you understand how flow fields rather work. And if you are coding along, you can inspect it from up-close on your computer and really see what's happening. Change the value of cell size, two, even larger number, and the mystery of making flow fields should be solved. I wanted to make it the lines of the grid thinner, but I don't want this change to affect the line width of the flowing lines. For that reason, I will wrap this entire code block between safe and restore. These two built-in Canvas methods, make sure that any changes we make to a state will stay limited to a specific area. Those changes can include any kind of settings such as fillStyle, stroke style, linewidth, opacity, global alpha, as well as things like rotation and scaling, which we are not using today. If you wanted to know how to use rotate and scale, you can check out my class on fructose. If I set the stroke style to write, it will only affect the grade, not the flowing lines, not the particle system. Save method creates a safe point, like in a video game, we can make any changes we want in here, e.g. linewidth, only 0.3 pixels. These changes will affect this drawing code and then restore. We'll reset all the changes made back here to the point it was when we call it safe. Because of that, when we later draw the flowing lines, the particle system using draw method on the particle class, they will not be affected by this stroke style and this line width at all. I wanted to keep the grid in our project, but I want a way for the user to be able to hide it and show it easily. I create a debug mode property called this dot d back will be set to true initially, same as we did with Colin in it here. When affect class gets instantiated, all the code inside the constructor will run. I can actually put any code I want in here, even an event listener, when we create an instance of effect class using the new keyword, at that point, I want an event listener for key down event to be applied. I need to make sure I still have access to properties on class constructor, specifically this debug property from inside of this event listener. If you use a regular callback function, you will have to bind it. Alternatively, I can use ES6 arrow function. These functions automatically inherit the skewered from their parent scope. So even when this event listener runs later, it will still be able to remember it was originally defined here, and it will be able to control this dot debug property for us. If you use regular callback function here, you will get an error because this keyword will become undefined. When key down event happens, we console log this auto-generated event object. I decided to call it by a variable name E, but you can call it whatever you want. If I open this event object down here, I can see it contains key property which tells us what key was pressed. That's exactly what I need. If E dot key is triple equals comparison operator to D, set this to debug value to its opposite. If it's currently false, set it to true. If it's currently true, set it to false. This is a very useful line of code that allows us to toggle between true and false easily. I only want this code to run if debug mode is true, I only wanted to see this flow field degrade when debug mode is active. Now, if you click on Canvas and press letter D on your keyboard over and over, you are enabling and disabling debug mode. We will use this debug mode again in the advanced class where I show you how to trace letter shapes and images. Now that the individual cells in the flow field are relatively large, we can clearly see that the lines break and change direction at the boundaries of each cell. Because each cell contains a different angle value, influencing the motion of these lines. Feel free to play with the code, give different value to cell size online 70. And then you can adjust for that change by tweaking this Zoom online 75, you can come up with some interesting combinations. Like here where I have a pretty interesting pattern that is clearly affected by the fact that individual cells in the flow field are 50 times 50 pixels. I will gradually increase the curve, the inner spiral of our sine cosine flow field. Beauty of math. Did you know that you could make art with math like this? 30. Randomized colors: I open another browser window and I will Google Color Picker. I'm looking for something like this where I can click anywhere I want and it will give me that color value. You can go into any color range you want. I take this color by copying it's hex value. I assign it as this dot color property on particle class. Inside the draw method on particle class, I said stroke style to this dot color. If the entire flow field was just a single color like this, there is no point Colin stroke style over and over. I would declare it somewhere, so it only runs once on the first page load to improve performance. But here I'm trying to give our particles a randomized color range. I create an array, I call this dot colors. I put this value inside like this. I go back to my color picker. I started from a dark color, so I go a bit lighter in this direction, e.g. and again, I copy this hex value. I put that similar but lighter color in the array as well. This dot color will be a color picked randomly from this dot colors array. I can access them through their indexes. This dot color index zero is this one. This node color index one is this one. Let's take another color and add it into the array. I choose one more color, go in even lighter. I choose one more. This time, it will be very light. You can choose as many colors as you want. It doesn't matter how many colors you add into this array. I want each particle to be randomly assigned one index from this array. How do I do that? Of course, by using Math.random between zero and the length of this dot colors array. However, Math.random, it gives us a floating point values. There is no index 1.5 in this array, so I need integers. I wrap all of this in Math.floor. And this is how you can randomly assign one value from an array of available values to each object created by a class. You can see that each line has a random value assigned, and our effect looks more interesting. Feel free to go back to the color picker and choose your own color range. It would probably be interesting to create sliders for this dot zoom so that users can easily control it. You can do that as a challenge in the end if you want. 31. Responsive design: If I resize the browser window, as with every Canvas project, canvas element will not resize automatically. In the same way we are applying EventListener for keydown event here in side effects class constructor. I can also create resize windows event listener. We are inside a class, so I don't want to be pulling width and height from the outside. So let's console log this auto-generated event object. I remove this console log. And I also remove this one. So we are Console login event object that was auto-generated when we resize the browser window and we triggered resize event listener. Inside I can see target of the event, in this case browser window object. And inside of that, we can see the new updated in your height and width. So we will just use these values. I console log e dot target dot inner width, and E dot target dot inner height. Now I can resize the browser window and I can see the new width and height. Perfect. So when we resize, I wanted to set width and height of the effect to these values. The problem is that we also need to resize canvas element itself. And I would again have to reach outside my class to grab it. I wanted to keep my classes modular. I need a reference to the canvas element inside my class, I refactor this code effect will expect only Canvas as an argument inside, we convert it to a class property. And from this dot canvas, now we can take width and height like this. I'm 69, now gives us access to the canvas element itself. I close the console. I need to take this canvas variable from line one, and I need to pass it to affect class constructor at the point when we create an instance of affect class using the new keyword here on line 135. Inside, it gets converted to this dot Canvas property. And from their width and height is extracted. So now when resize event fires, I will call this dot resize method, which we will write. Next. I pass it the new width and height of the resize the browser window. We get these values from this auto-generated event object. I will write this method down here. Inside the effect class. We know it expects the new resized width and height as arguments. We have a reference to the actual canvas element here on line 69. So I take it, and I also take width and height. I copy these three lines and I paste them down here. When we resize browser window, we get a new width and height. We set width of canvas element to the new width. We said height of canvas element to the new height. Then we set width and height properties of the effect to this new updated values. If I resize the browser window, it looks interesting, but I'm not sure if we are not wasting memory like this. If I go back to a smaller size, lines should eventually move back to the smaller available area. And then when they get more space, the pattern should expand to the larger space. Again, I think the way I prefer to do this is to call init method. Again, after the window was resized. Don't do what I'm doing now. Don't run this code because if I call this.in it from resize method and I re-size browser window. Something very bad happens. We are pushing more and more particles into this dot particles array, and the effect slows down considerably. If I go up to init method, you can see that every time init method runs, we delete the flow field array and we refill it with new grid of angles. I need to do the same with particles. We set this dot particles to an empty array, delete an old particle objects inside, and we create a new set of particles. Performance wise, this is not the best solution. We don't need new particles. I could have just created a reset method on each particle. And I could have reset particle positions here rather than deleting all of them and creating new ones to replace them. We could also use object pooling technique here if we need to increase and decrease the number of particles depending on canvas size that I'm doing now works because I'm calling this dot init method from inside resize on line 126. When we resize, we delete old flow field, degrade and replace it with a new set of angles now adjusted to the new canvas width and height. And we also delete all particles. And we create a new set with random positions spread over the new available canvas area. Of course, for this to work, we need to make sure we call this.in it from resize method here, I assume most users will not be resized and the page over and over. So the solution is perfectly fine. 32. Experimenting with flow field patterns: The shape of the flow field is what I call Sine and Cosine of spiral curve pattern, bit of a tongue twister for people with mild language background. I'm not sure if this shape has an official name. If it does, let me know, please. It's the pattern you get when you use increase in positions in a two-dimensional grid as angle values passed to sine and cosine. We see the formula here. This line of code that determines the shape of the pattern. We can play with it in many different ways and get very different results. Look what happens if I replace multiplication with addition here. Or what about if I do that here? Or I try this? I adjust the zoom. Interesting. I can also adjust the curve. You can just randomly change the operators here and maybe you will stumble on something interesting that I haven't tried. Again, this is how I can slowly zoom out of the pattern. I can increase curve. And because of the changes to operator we made when calculate an angle grid now changes the direction of the entire pattern. This looks nice. I could maybe use this effect for space background in the upcoming game development class. I could even make this react to some objects. We will learn more about that in the next part, where I show you how to make the lines flow around text. Surprisingly, doing less equals here will also change the pattern. It will become more apparent when I zoom out. I think this is because we change at which point the cells in the grid break to a new row. If I go back to multiplication here we are back to the usual spiral pattern. I'm just playing with it now. I try change in Zoom. I change curve. I can also do both sign here. Or I can swap sine and cosine. I think it's clear how this works. Now, feel free to play with it and do your own experiments. 33. Drawing text: Down here on the main effect glass, we have the init method. It splits into a grid and creates angle values using this trigonometric formula. I still wanted to split into grid first. Then instead of this algorithm, I want to draw text on Canvas and calculate angle values of the vector field grid from there. Let's draw that text first. For clarity, I will put that code in a separate method. I will call it draw text. I will need access to CTX context object from here so that I can sit Canvas properties from here. At first, I will just pull CTX variable directly from line two. We will re-factor this in a minute. From there, I access font property and I set it to 500 pixels impact. I want to large bold letters. Then I call built-in filtText method, which expects at least three arguments, the text to draw and x and y coordinates where to draw it. The text I want to draw is the JS, because we are learning about JavaScript today. Horizontal coordinate will be this dot width, the width of Canvas times 0.5. The middle of canvas horizontally, vertical y coordinate will be this dot height times 0.5, the middle of canvas vertically. As you can see, I'm pulling the CTX variable from line to directly inside my class. I want to follow good practices and keep our classes self-contained. So I will re-factor this. I will need access to CTX from Draw text method as well as from init method because that's where I draw text method will be called from. I also need access to that same context from draw a grid method. Since we need CTX in so many different places all over the effect class, I will choose this following solution. We have ctx here on line two. I will make sure affect class constructor expected as an argument. And inside I will convert it to a class property called this dot context. Like this. We will use this dot context here and here inside the draw text method. Inside the drug rate as well, it will no longer be expected as an argument coming from the outside. Instead, in all of these places, I will use this dot context class property we just created. We also use context inside render. It will no longer expect context as an argument. I will also remove contexts argument passed to draw method on particles and here past to draw a grid. I think I made a few mistakes here. Bear with me a second, please. I actually have to pass this law context to draw method on particles like this. Okay, I run the code, I get an error. I open the browser console effect class constructor expect CTX and converts it into this dot contexts property. I need to make sure I pass it CTX down here on line hundred and 51 when I create an instance of that class, also, I no longer needs to pass CTX to render method. Here. I take the draw text method and I call it from inside the render like this. When we pass x and y coordinates to filtText method, these coordinates set anchor point of that text where the text is drawn in relation to that anchor point depends on the value of texts, line and text baseline Canvas properties. Text align handles horizontal alignment. We set the anchor point of the text to the middle of cannabis. So look what happens when I set the text line to center. I can also use text baseline property to align the text vertically in relation to those anchor point coordinates. I set it to middle. One more time. When you draw text on cannabis, the values you pass to filtText x and y coordinates defined the anchor point where the text sits in relation to that anchor point depends on which values you give to text, line and text baseline properties. By default, the text is left aligned horizontally and alphabetic baseline vertically. We set them to center, middle to get centered text like this. This is very important to understand if you ever need to work with text on HTML canvas, I will set the debug to true to enable debug mode, I want this text to be visible when in debug mode. Oh, I have an error. Console is telling me it's here. I need to use this dot context here as well to draw my grid. Probably already noticed that before. So when we are in debug mode, I want to see the vector field degrade. And I want you to see this big white letters that will determine the shape of the flow field. Being able to show and hide this text easily by pressing letter D on keyboard, by entering the debug mode will make the effect easier to understand and modify. Inside render. I say if this is the debug is true, only then draw a grid and draw text. Now, you can press letter D over and over to toggle debug mode on and off. 34. Understanding pixel data: Inside init method, I want to draw to x at this point. Remember that init method will run a just once on the first page load and it will create a vector field and particle system for us. After I draw these big white letters on Canvas, I want you to scan Canvas for pixel data. My ultimate goal is to know which cells in the grid are overdue letter shapes, and which grid cells contain just the black empty background. I do it by scanning cannabis for pixel data after I drew that text on it. And I will analyse that pixel data and I will map it on the grid. I will call built-in get imageData method. This method returns an auto-generated image data object representing the underlying pixel data for a specified portion of cannabis. It expects four arguments to specify which portion of cannabis we want to analyze. I want to pixel data from the entire canvas element. So from coordinate zero-zero to this dot width, this dot height. In other words, get imageData. I will scan a specific portion of cannabis for pixel data, we can extract colors and coordinates of each pixel from it. Do that, I need to understand how is the data in this auto-generated image data object organized? I will assign this to a constant variable I call e.g. pixels, and our console log it. I can see the image data object here in the console. I open it and I can see it has width and height of the scanned area. This is important. We need to know these values so that we can extract x and y coordinates of each pixel from this. If we know the width, how many pixels we have, very row of data, we know after how many pixels this array breaks to another line. Canvas is scanned. And in the same order we created our angle grid row by row from top to bottom, going left to right on each row. If I open this data array, this is where all the pixel information is stored. It's actually very simple. It just holds a long sequence of integers. My browser console splits this extremely long array into blocks, but it's actually one long line of numbers. How does that translate to pixels? I can see that this is a special type of array called UI and T8 clamped array. It can only contain unsigned eight bit integers clamped to arrange 0-255. We know from RGBA color declaration in CSS that each color can be represented by a combination of red, green, and blue. Each color has a value 0-255. And different combinations of these allow us to create any color in CSS RGBA color declaration. The Alpha opacity value is 0-1. Here in this pixel array, alpha is also represented by a value 0-255. We need to remember that pixel data array is an extremely long line of numbers where each four values represent red, green, blue, and Alpha over a pixel, a single pixel. So this is pixel one. The next four values, our pixel two, and so on. I can see the width of the scanned area is 528 pixels and the height is 464 pixels. 528 times 464 is 244,992 total pixels scanned. And we know that each of these pixels is represented by four values in pixels. Array. Value for red, green, blue, and Alpha. 244,992 times four is 979,968. Here we can see that the pixel data array has exactly 979,968 elements. So that checks out. If I look somewhere in the middle of the array, I should be able to find these white pixels that make up the letter shapes. Here I can see that we have to 55 to 55 to 55, which is white, and 2554 alpha fully visible. We know that these four pixels with index 533,600 in pixels data array represent pixels somewhere in this white area. If I change the fill style too, right? I opened the array. Mostly I see black pixels are zeros, zeros 00 opacity because the background of our canvas is transparent. Here I can see those red pixels to 55 read zero, green, zero blue to 55 Alpha. These values represent a fully visible red pixel, somewhere in the red area, we know these pixels are the text drawn on Canvas. 35. Handling pixel data: Okay, so init method will run all the ones on the first page load and inside we are trying to create a grid of angles, a vector field. We drew text and we scanned us with that text drawn on it with built-in get imageData method. Now, we are holding all pixel values inside this pixels variable. I will comment out this code which calculated spiral angles for each cell in the grid using our special sine and cosine formula. Instead, I will make the shape of the text of the letters to determine angles held in each cell of the vector field. To do that, we need to use nested for loops. Again, the outer for loop will cycle over Canvas row by row, jumping by cell size. Each time we enter a new row, we set the cell size to be 20 pixels. Here on line 75, I can show and hide this grid made out of 20 times 20 cells by pressing letter D on my keyboard. Inside, the inner for loop will cycle through each row horizontally from left to right. Also jumping from cell to cell by cell size value. Hopefully now we understand how these two nested for loop cycle over the canvas row by row from top to bottom. Every time we enter a new cell in the grid, we need to calculate an index. We already calculated index up here on line 40. This formula helps us to convert x and y coordinates to a single value representing index in an array. As we cycle over the grid cell by cell, row by row, I want to match that cell position with an index corresponding to these coordinates in pixel array so that I know what pixel data was scanned for this particular cell. A match in x and y coordinates of the cell. I am currently cycling over with these nested for loops to a specific index in pixel array to extract pixel data for this canvas area. Again, this is that formula where we multiply the row number times the width. So we get pixels per row plus x, which represents however many pixels we are currently into new row. This time, we are trying to match index in pixel data array. We know that each particle is represented by four elements, four integers, 0-255, represented in red, green, blue, and Alpha. So to get that correct index, I multiply this times four. Now I know the index in pixels array that corresponds to the x and y coordinates of this particular cell in the vector field, the grade. I want the color I start with, right? Red will be pixels array at that index. Green will be the one index that comes after. So pixels index plus one. Blue is index plus two. Then we have Alpha, index plus three. Then we get red value of the next pixel, green, blue, alpha, and so on and so on. So we have red, green, and blue of each pixel. Combination of these values will give us the color of the underlying pixel. Right now, all the text pixels are red, but we will change that soon. 36. Converting colors into angles: My goal now is to make the color of the letters affect the direction of the flow, the angle in the grid of the vector field, e.g. red pixels push the flow to the right. Blue color flows down, green color flows to the left, e.g. and all the color values in-between give us angle somewhere in-between these directions. There are many ways I can convert colors into angles, and we can achieve many different effects here. The easiest way would be to somehow converted the three values of red, green, and blue into a single number. I can e.g. generate a grayscale value from this RGB colors are calibrated in a way that when all three colors for red, green, and blue are equal, the resulting color is a shade of gray. The color is somewhere on the gray-scale spectrum without a bias towards red, green, or blue view. We can convert to grayscale by getting the average of all three, red plus green plus blue divided by three. Now, I could create a grayscale effect from this by taking that average value and applying it back to red, green, and blue. But I actually just wanted to take that single average value and I want to convert it to an angle value. We have a range of grayscale color which goes 0-255. And I want to map these values two angles, 0-6, 0.28 rad, 6.28 rad convert to 360 degrees, a full circle. So colors can flow in any direction depending on the gray scale value of that area, since we are only operating in positive values. So the formula is very simple. Take the ratio between the current gray scale of this cell in the vector field and the max value. And apply that ratio to that angle value. Let's say if grayscale is 127.5, that's halfway 50 per cent in between the minimum value of zero and the maximum value of 250. 527 point 5/255 is 0.5. We have the ratio, and now I multiply it times the maximum angular range of 6.28 rad, which will give us half of that value, 3.14. 3.14 is pi half circle. By the way, if gray scale is e.g. 63.75, 63.75 divided by the maximum 255 value gives us a ratio of 0.25, 25 per cent towards the max value, one-quarter. We apply the same ratio to max angle value 0.25 times 6.28 gives us 1.57 rad half a by quarter circle. Basically I'm taking the ratio, the relationship between the current grayscale value and the maximum possible color value of 255. And I'm applying that ratio to angle range 0-6, 0.28 rad. Because we use gray scale like this, the resulting flow will be influenced by how light or dark the colors are on the gray-scale spectrum. I will show you what I mean when we get the animation going. I want to limit how many decimal points we get here as there is no need to calculate tiny angle values. Two decimal points is fine. I put the entire expression in brackets, so I can call built-in JavaScript to fixed method on it. I pass it to, I want only two decimal points. Okay, So as the init method runs, we set flow field to an empty array. Down here, I will fill the array with a grid of angle values built-in array push method. And we will push an object with x property set to x-coordinate, horizontal coordinate from the inner for loop as we jump by cell size within each row cell buy-sell, y will be y index from the outer for loop as we jump by cell size value over cannabis row by row from top to bottom. Color angle value is this grayscale color range converted to an angle value in radians, which will make sure that particles flow in a different direction over different colors. In console, we can see that image data object. I actually don't want it to be pointing to the entire object, but only to the pixel array, which is held here inside the data property. Pixels will be imageData object returned by get imageData built-in method dot data. We are pointing directly to this array now. Now we see it in the console here. Flow field array now contains objects with x, y, and color angle properties for each cell in the vector field grid. We need to make sure we account for this change when updating particle positions appear inside update method on particle class. Here we are pulling angle value from flow field array dependent on particles current X and Y position on Canvas. Now, there is an object with three properties at each index, so I need to access its color angled property here. Like this, nice, something is happening. We have two problems to solve. I only want to run this code block that matches particle position to an object representing a cell in the flow field. If there is an object at that index on the Dan pulled the color angle, that will fix the issue where particles move outside the bounds of Canvas between recites. The second issue, if you are coding along, you can zoom out and you can see we are already draw in a flow field shaped as letters JS, but for some reason it's skewed to the right. Why is that? I will make the font smaller so we can see we have the shape, but why is it distorted like this? I'm making the cell smaller. Now we can clearly see the flow field is shaped as letters JS, but it's skewed in a strange way. It actually took me a few minutes to realize what's going on, but let's fast-forward to the solution. If you know why it's doing that before I reveal it, pause the video and type it in the comments. Right now, you see up here we are setting canvas width and height to the current width and height of the browser window. The problem is that when we create the flow field, if cell size value on the main effect class, the width and height of each cell in the grid is not a number that canvas width can be divided by. Without any remainder, we will get some leftover pixels when we break line as we scan Canvas row by row from top to bottom. As we go down row by row, those leftover pixels at the end of each row accumulate to the flow field degrade. And the flow field degrade. The number of elements in there doesn't exactly match the number of cells we can fit on Canvas. So the rows get pushed more and more to the right. There are multiple ways to solve this. We just need to make sure that canvas width is divisible by cell size without any remainder, without any leftover values. I will solve it by setting canvas width to a specific size. But you can also use a dynamic formula here using the remainder operator. If you wanted to keep it full screen, if you want to keep the responsive functionality. I said canvas width to 600 pixels. And because the cell size on line 75 is set to 20 pixels, 600 is divisible by 20 without any remainder, so we are getting the correct shape. I will also set canvas height to 600 pixels, or maybe 500 times 500. When I resize Canvas, our customer resize method gets triggered and it breaks the shape again because it allows canvas width to be something that is not directly divisible by cell size without an remainder, I can either make sure it only research kinda size if the new width is divisible by cell size. Or I can just comment it out for now, down here on line and 92. And because Canvas is 500 times 500 pixels, I want it to be centered exactly in the middle of the page. I can do that with CSS. It has positioned absolute already, so I give it a top 50 per cent, left 50 per cent, and transform translate -50% -50 per cent. Of course, you can position it in a different way depending on where you want it. I will now just set the background on the body element to black. I think this looks alright for now. I increase the font size. 450 maybe. Yeah, we are converting color to an angle. So look where the particles are flowing when the text is right. And look what happens to their direction of movement. When I change the color to green. Blue angle is actually similar to read it because we are not spreading the color range itself, but we are converting it to grayscale first, and then we are converting that to angle. It seems like blue and red have a very similar grayscale value. You will see the biggest difference in angles when using colors that are light or dark. Colors that are far apart on the gray-scale spectrum. Brown. Let's try something much lighter, yellow. We can clearly see the direction of the flow is influenced by the color, by the text color, perfect. 37. Flow and gradients: I could leave it here, but I was playing with it and I came up with more really cool things I want to share with you. This is always my favorite part where we have all the logic in place. The effect is running and I can experiment. We know that the different colors push the particle flow in different directions. So let's see what happens when the fill of the text is not just one color, but a gradient that transition from color to color. I wonder what that will look like. A custom variable called the gradient one, and I call built-in Canvas create linear gradient method. It expects four arguments, x and y of the start point, and x and y of the end point. Gradient will be drawn along a line between these two points. I want a gradient from top-left corner coordinates, zero-zero towards the bottom right corner coordinates effect width and affect height, which are the same as width and height of the canvas. Now I can take that gradient one variable and I call built-in add color stop method from it. At offset 0.2, the color will be white, e.g. 0.8 offset. I try full blue. Now I can just set fillStyle to this gradient one variable. Awesome, this is so cool, like how the flow curves as we go from blue to white. Now that we understand this, we can experiment with different color ranges and different gradients. I add another color stop at the zero-point for yellow color. 0.6 will be e.g. green. I can just play with the color values to see what flow I get. It's interesting how the flow direction changes as the colors transition following the gradient. I forgot to mention one important feature of filtText and stroke text built-in Canvas methods, we can pass them optional fifth argument to define the max width of the text. I will say that max width of the text is the weight of canvas. Now, if the text is longer, it will be squashed to fit into the area. If I want some of margins, I can say this dot width times 0.8. And the text will take the maximum of 80 per cent of Canvas. It will take 80 per cent if we give it more space by setting canvas width to a larger value. I go back to Canvas size 500 times 500 pixels. I changed the word to flow. We can change font to Helvetica, e.g. back to impact. And texts will be JS. I want it to flow to curve more to the right. So I play with the color values to get the gradient influence the direction of the flow field. This looks nice. I copy this code block and I rename all of these gradient too. And I use that as fillStyle online 112th. I play with the values again to see what happens. I roughly know which direction each color flows, but it's mostly trial and error. This looks interesting. I create a gradient three by copying this code block and using that as fillStyle online hundred and 18. This time it will be a radial gradient, so we need to pass it six arguments, x and y of the central point of the inner circle. It's radius. So I wanted the inner circle to start exactly in the middle of canvas horizontally and vertically. And radius will be ten pixels. The outer edge of the radial gradient circle will be again from the middle of Canvas and radius will be a desktop width. Interesting. I adjust the colors to see what shape I get. You can see how the radial gradient affects the flow in a different way. I really like this one. I will keep all three gradients I defined so I can swap between them later if I want to. 38. Tips, tricks & experiments: When angles change direction, it's a sharp turn. It might work for you, but maybe we want the lines to turn more gradually when the angle in the vector field changes. We are holding the current angle value for each particle here on the main particle class. We are changing this value inside update method to match the angle value of the grid in the flow field. I will create another property here called new angle. And I will check the difference between the current angle and the new angle. And instead of changing to that new angle instantly, when particles enter a new cell in the flow field grid, we will just push particles towards that new angle by a certain fraction of their difference. Or to reduce the calculations needed, rather than calculating the fraction of the difference between existing angle and the new angle, I can just have a set value called angle character. It will be a small value in radians. Down here in the update method, I said new angle two, an angle stored in the cell of the vector field. And I say if the current angle of the particle is more than new angle, reduce the angle value of the particle by the angle character value we defined, move the existing angle one step closer to the new angle, I choose to do it this way to save performance. You can also say change angle value by one tenth of the difference between angle and new angle, which will give you more gradual curves at the expense of performance because more calculations are required. Else if angle is less than u angle, increase angle value by angle character. One more elsewhere we set angle to new angle. Now you can see the sharp turns are gone and we're just gently pushing the particles towards certain angle values. I adjust the angle character value to push them a bit harder. Or we can do a random value here between some small range so that each particle curves differently. I can adjust maxlength. I can change the speed modifier. We have all the logic in place. If you want, you can play with it to see what variations of this effect you can come up with. So this is my very basic performance efficient way, how to make particles change directions more gradually as the flow over the grid of cells in the flow field. I would also like to be able to control how many particles flow inside the letter shapes and how many flow in the area outside of the letters. Now, it's completely random, but it doesn't have to be when the particles reset. I wanted to check if it's resetting within the letter shapes or outside of it, somewhere in the transparent area around the letter. If it's outside the letters, I wanted to try again. I will have a variable called attempt. It will start at zero. At first. I will have a flag called reset success. Initially it will be false. When the particles reset inside the letter shapes, we will switch this to true. I will have a while loop. When the particles reset, I wanted to try five times. It will make five attempts at randomly picking a position somewhere in Canvas to try to reset within the letter shapes. As long as attempts are less than five and as long as reset success is false, I will create a test index. It will be a random cell somewhere in the flow field array. I check if this test index has alpha more than zero, which would mean that it's the cell in the flow field that has some visible color in its pixel data, which would mean it's one of the letters. Alternatively, it will be transparent, which would mean it's the empty background around the letters. If it has Alpha more than zero, it means we found one of the letters cells. So we set x and y positions of the particle to x and y positions of the cell in the flow field. The grid. Down here I can see that fluid-filled cells, objects inside flow field array contain x, y, and color angle values. So this X and Y will be coming from there. I said that those x and y coordinates as the first point in the path by adding it to the history array. And I reset the timer back to maxlength terms two. I will also set reset success to true. Importantly, I need to make sure every time we try to do this, we increase attempt by one so that when we reach five, this while loop, and we don't want to create an endless while loop. It will continue only as long as both of these conditions are true. So only when attempts are less than five and at the same time, as long as the reset success is false. If I run the code, it doesn't reset at all. So that indicates this condition on line 78 is always returning false. It's because we are not pushing this Alpha value into our flow field cell objects. I changed attempts up here to 50. So down here I'm calculating alpha online 160. I need to make sure that the value actually ends up inside flow field array. Like this. Perfect lines are resetting inside the letters. I said max, attempt back to four after the while loop has finished. If after trying to reset four times, we still haven't found a cell with alpha more than zero and reset, success is still false. We will restart x and y coordinates of that particle somewhere randomly around the canvas. It's still not resetting particles outside letter shapes. Even when I go down to just two attempt. How about only one attempt? I will figure this out. Just give me a second, right? I have to put these new positions inside history array. And most importantly, I have to reset the timer back to maxlength times two for the particles to animate. Nice. So now when particles reset, this code will attempt a certain amount of times to reset the particles inside the letter shapes. And if it doesn't find that the cell in a certain amount of attempts, it will just reset the particle somewhere randomly around Canvas. We can tweak how many particles we want to reset inside and outside letter shapes by changing the number of attempts on line 75. The last attempt, the less likely particles are to find the letter shapes in time. So they might reset outside. I can increase the number of particles and I increase the cell size. We can see most particles now reset within letter shapes, but occasionally we get one reset in somewhere else, somewhere outside, you can adjust the number of particles and cell size to whatever you want and whatever your computer can handle here. I can remove this code. I think we all now understand how these values affect the animation. Maybe 2000 particles. What if I add white color to the colors array for some highlights? Interesting, I remove it again. I try gradient one to change the direction of the flow field. Gradient too. I think my favorite is gradient three. Which one do you prefer? I increase maxlength, which does have effect on performance. Longer lines take more computing power to draw. So we create a grid, we draw text on Canvas. We analyse that kind of us with texts drawn on it. For pixel data, we cycle over that pixel data in a grid to extract color values and coordinates of each pixel. And we convert these result in colors into angles. From that information, we create a flow field, a grid of angles, where the direction of angles is influenced by the text that was drawn on Canvas. I can remove this code block. Then we create a particle system here. Maybe I wanted the particles to start flowing from the letter shapes even on the first page load. So I will manually trigger reset method on each particle as I create them, which will force them to make a few attempts to find cells in the flow field that contains some color values, cells that are over the letter shapes. I changed the text to flow. You can see that it messes with the radial gradient, which is expected. Increase canvas width to 1500s pixels. I can create max-width of the text down here. I can use gradients to, to change the flow direction. This looks interesting. How about gradient one? You probably realized that this would work on images as well as I'm just using get imageData to get shapes of letters that were drawn on Canvas. Do you want to learn how to use this on images? I will show you exactly how to do that in the next part. I hope you're having fun. 39. Image flow fields explained: So we built a text flow field and we experimented with it. I put the text back to capital js so it fits into the screen and we can see everything. You can download images I will be using in the resources section below, or you can use your own. I will be using small images, 200, 200 pixels with a size around 10 kb. Size of the images matters for performance. So if you are using your own images, think about that and optimize the images before using them for this project, if you want your animation to run smoothly, this effect looks great with some specific images. From my experience, the best result is with simple shapes that have gradient colors, as this will make the flow curve around it nicely. In index.HTML, I create an IMG element with an ID of star and source will point towards that image. If you remember from the previous part, the trick was to draw text on Canvas and then analyse that pixel data and create a vector field, a grid of angles from that shape. I want to do the same thing here with an image. Now, I wanted to draw this image on Canvas and then I want to scan that kinda us with this image drawn on it. The problem is that there are some limitations to this Cross-Origin Resource Sharing is a security measure that it doesn't allow us to draw any random image on Canvas and then to scan that Canvas for pixel data. If the image is not from the same origin, that kind of us is considered tainted by cross origin data. And your code will end with a console error to draw image on Canvas and the scan it with get imageData, that image needs to be considered same origin. If you are running your code from a server and image source here points to an image stored at the same server address. Then it will work. I want this codebase to be flexible and to easily work locally. I don't want to go over setting up a local server right now. So one possible work-around is to convert the image itself into a so-called base 64 data string. To do that, we can use to data URL built in JavaScript method, or there are many websites that will do that for us in seconds. To sum up, to avoid any cross-origin resource sharing issues, I am converting image file itself into a data string, and then I will use that data string as image source. I do that by Google and P and G, two base-64. Note that the image doesn't have to be PNG. It will work with other formats as well. I will choose e.g. this website from online PNG tools.com. But the other sites will work in a similar way. I drag and drop the image I want to convert from my computer into this field. And it will instantly convert the entire image file into a very long line of code. Use a small image as possible in terms of width and height, as well as kilobytes. Otherwise, this code will be too long and it will cause performance issues when we animate it in a minute. Ideally, use the images and providing. And then once you have a working effect, you can swap to a different image file. This base-64 data string needs to start like this data colon image. If it doesn't for you, click this checkbox to convert it to a specific format that we need. I copy this extremely long line of code and I paste it as a source attribute here. In Visual Studio Code, I can click View and word wrap to make the code file more manageable. As you can see, we are drawing that star image, but we never use the actual image file. This long string of code completely replaces the image file itself. And since all that code is inside our index HTML file, it is considered the same origin in all circumstances. And therefore we can do advanced pixel operations on it with HTML canvas without triggering tainted Canvas error. Let me show you. It's easy to get this to work now, I don't really want to draw the image element itself, so I take its id and I hide it with CSS. Instead, I want to draw it on Canvas where the JavaScript, I go to the main effect of class and I create a new property called this dot image. I pointed this variable to that image element using its ID as usual. So we have a variable pointing to the star image here on line 110. Let's draw it on Canvas. Up here I said canvas width to 500 pixels. Same as I have this draw text method. To keep our code organized, I will create a separate method to draw the image. I don't want to call it a draw image because that name already exists for a built-in Canvas method, I will call this custom method e.g. draw a flow field image for clarity. Inside, I take this dot contexts and I call built-in draw image method. It needs at least three arguments, the image to draw and x and y coordinates where to draw it. To test it, I will call draw flow field image from inside render because I am not a defined in width and height online 148, the image is being drawn at its original size. We can resize it or stretch it by passing in values for width and height, 300 with 100 height. How about full-width and full height of canvas? Nice. What if I wanted the image to be centered exactly in the middle of cannabis, regardless of which size I choose. That's also very easy. I create a helper variable called image size. Let's say I want width and height of the image to be 70% of canvas width. I use it here as width and height. Yes. Now I said x-coordinate to the middle of cannabis. So this dot width times 0.5 and I do minus half of image size. The image is centered horizontally. For vertical y coordinate, I do height of Canvas times 0.5. The middle of kind of as vertically minus Image Size times 0.5. Perfect. This is a very simple formula to center image in the middle of HTML canvas. It accounts for the fact that images on Canvas are drawn same as rectangles from the top-left corner going right and down, depending on their width and height. If I resize it now to any size, it will always be drawn in the middle of Canvas. I will do 80 per cent of canvas width, e.g. as I explained before, here we create a flow field degrade, we draw something on Canvas. Then we analyze that kind of us with that text or that shape or image drawn on it for pixel data to create a vector field grid from it by converting colors into angles. We already have all this logic from the previous part in place. So all I have to do here is to replace this part where we draw text with code that draws the image. I call draw flow field image here and here we go. That was easy. I don't really want to draw the actual star image over it like this. Instead, I take it from here and I want to draw it behind the particles only when debug mode is active. So from within this code block, now we can toggle debug mode on and off by pressing letter D on the keyboard to show and hide the vector field, the grid, and the source image. I can change the image size. We can see it always stay centered and that it all works nice. If I resize Canvas, it will resize the image with it. Because I said that the size of the star image to be 80 per cent of canvas width. 40. Color manipulation tricks: I couldn't leave this and let you do your own experiments. But again, I was playing with the code a lot and I was wondering, how can I preserve and use the original colors of the source image. Right? Now, we are using colors from this dot colors array on line 25, the colors of the source image itself are not visible. Other than using them to determine angles of the flow field. We are scanning that image for pixel data already, so why not try to use it? I wrote multiple different solutions to this end. I decided to show you this one for the reasons I will explain as we implemented, if you have some ideas how to improve this code base, please let me know. On each particle object, I will store a separate property for red, green, and blue. This is not needed if I was just taken the colors as they are from the original image. But when you see what that looks like, you will probably agree that we need a way to dynamically and gradually transition between colors as the particles and lines travel from cell to cell over the flow field. That's why I will need all three colors separately like this. Let me show you why. I will concatenate RGB color declaration here. Feel free to use a template literals here instead, if that's your preferred syntax, I will simply do RGB plus, plus coma, plus green plus coma, plus bu plus closing bracket. Now, we can change the color of particles in there by setting these two different values. Okay, that works well. I go down here inside init method where we drew that original image file on Canvas and we scanned it for pixel data. I will need three values for red, green, and blue. So I make them available by pushing them as additional properties inside flow field array. So now it's kind of as a scanned for pixel data cell by cell, row by row. Each time we enter a new cell in the flow field degrade, we will store all this data for each cell in the grid inside our flow field array. Now I know what is red, green, and blue value of each individual cell in the grid. So I can match particle color to these values as particles flow over Canvas. Inside the update method on particle class. In this area, I extract the element at this particular index inside flow field array into a variable just because I wanted the syntax in this code block to be easier to read. And also for performance, I pulled that index from the array only once, and then I use that existing reference in multiple places. Now I can replace it here and here. This is the same code. We just refactored it. So in this area, we extract angle value from fulfilled array to handle particle motion. In this area, I will extract color values of that cell in the flow field. I only wanted the cells that contain pixel data of the image if they have alpha more than zero, because I know the transparent cells with zero alpha or the background around the image. We don't care about those here. So if this cell is not transparent, take this dot read property on this particle object. We defined on line 25 and set it to read property we just extracted from flow field read property contained in this particular cell in the flow field that the particle is currently moving over. We will do the same for this dark green and this dark blue. Now, I want to use this updated color values to construct RGB color string again. So I copy line 28 and I use it down here on line 62. Nice. We are seeing the actual colors of the image. Doing this as a lot of extra calculations per animation frame to our effect. But it's up to you to decide if it's worth it or if you prefer to use predefined colors like we did before, I'm just showing you what's possible. As usual, all of this can be optimized in many different ways. Advanced optimization techniques are outside the scope of this class, but we can talk about it later in some other course. You can see the lines are blinking. Do you know why that is? It's because when the particle travels from blue cell to white cell, e.g. the color of the entire long line that the particle is dragging behind it will change to that new color instantly. It will instantly change color of the entire line because each line is just one draw call. So we can just change the color of a part of that line with the code we wrote so far. Again, multiple solutions here. I could adjust our code to check if color changed. And then use that updated color just for some line segments. Or what I will do here, I will change the color of the entire line. But instead of that color change happening instantly, I will gradually calculate the difference between the two colors over multiple animation steps. So the colors will not just swap instantly, they will transition slowly. How do we do that with code? Since we are in the advanced section, I will use more advanced simplified syntax. This is called a ternary operator, the only JavaScript operator with three operands. We will use it here as a simple one-line if else statement. Define a condition, an expression to evaluate. If it's true. Question mark, do this. Else, if it's false, colon, do this. So as the particles flow over the flow field grid and they extract color values from those cells. I check if red value of the particle is the same as the red value of the cell in the flow field at this index. If it is, just don't do anything and leave it alone. If the red value is not the same, instead of instantly reassigning it to the new updated red value, I will just gradually increase or decrease red value by a fraction of their difference. Let's say by one tenth of the difference. We could have done the same for angle values here, but I decided it's more performance efficient to use a set value stored inside angle character, although using gradual angle differences would create more natural curves in particle motion. You can try that later if you want. Anyway, I check if red is the same as new red. If it is, leave it alone. If it isn't, bring them one step closer together, but not all the way at once to get a gradual transition. I can't see if this is working or not. So let's do the same thing for green. Like this. I have to be careful about the syntax here. I think it's working. Let's also do it for blue. Like this. I'm just checking my code to see if everything is okay after all that copying and pasting. Yeah, this is good. I'm using 0.1 here. This value will determine how sharp or how gradual the color change will be, how fast the colors will transition from the existing color to the new target color. Before the transition of color happened instantly for the entire multi segmented particle line. Now they happened gradually and the effect looks nice. I think this is really special. I can't believe we went from drawing a single animated line to bend in flow field shapes around images. If you followed all the way here, this is a great achievement, well done, and congratulations on your dedication to learning. You are a creative coder for sure. Here on line 86, I can control how many particles restart within the shape and how many restart outside of it by changing how many attempts we give to each particle to try and find that shape between resets. To make sure this works, we can use a different image. Again, keep in mind too small images if possible. Simple shapes like stars work great for this effect. You can download the other image I provided in the resources section, or you can use your own image. Now, I copy this extremely long line of code and I go to index HTML. We need lines to be breaking to replace the source. So if it's all on one line for you, you can go to View word wrap to make VS Code Editor brake lines like this. To replace the data string. In Visual Studio Code, I can just select the start of this very long string. I scroll to the end. I press Shift key on my keyboard. And while I'm holding Shift key down, I click where I want the selection to end. It will highlight all the texts in-between and I can press Delete. Nice. Now that I know I deleted the old image, I paste the new base 64 data string from the new image. I save it. And here we go, we are drawing a different image and we can see the colors are being correctly extracted from the source pixel data. You can check out the source code in the resources section below. I will also play with the code now and I will include some of my other bonus experiments for you to download and inspect if you want inspiration on where to take this effect further. I like creative coding projects because we are learning about animation as well as the general program and principles. We did a lot of array manipulation functions and for loops today, e.g. these are all very important in every branch of coding, not just for animation. Thank you so much for spending your time with me. I hope you got a lot of value today. I'll see you later.