Develop Animated Bubbles with HTML5 Canvas and JavaScript: A Step-by-Step Tutorial
Tags: javascript, programming, beginners, webdev
Published: 2023-08-31 15:16:00

Hello everyone! Welcome to this tutorial where we're going to dive into the world of creating fun bubbles in code by using HTML canvas and JavaScript. The best part? We'll achieve all of this using just a touch of HTML and all JavaScript, no CSS.

### Unveiling the Concepts

Today, we're going to master the following concepts:

1. Creating circles using the `arc` method of the canvas context.
2. Utilizing the `requestAnimationFrame` function for smooth circle animations.
3. Harnessing the power of JavaScript classes to create multiple circles without repeating code.
4. Adding stroke styles and fill styles to our circles for a 3D bubble effect.

You can follow along with me, or use [the final codepen](https://codepen.io/shrutikapoor08/pen/wvQXMVO) if you want to take a look at the source code

If you prefer to learn in a video format, follow along this video 

{% embed https://www.youtube.com/watch?v=IjPgXP3gDyI&ab_channel=ShrutiKapoor %}

### Getting Started

First things first, we need an HTML5 Canvas element. Canvas is a powerful element for creating shapes, images and graphics. This is what the bubbles will be created on. Let’s set it up -

```html
<canvas id="canvas"></canvas>
```

In order to do anything meaningful with canvas, we need to have access to it’s `context`. [Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) provides a interface to render objects on the canvas and draw shapes.

Let’s get access to canvas and it's context.

```js
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
```

We will set up our canvas to use the entire window height and width -

```js
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
```

Let's give our canvas a nice soothing light blue background by adding some css. This is the only CSS we are going to use. You can also do this with JavaScript if you wish.

```css
#canvas {
  background: #00b4ff;
}
```

### Time to Create Bubbles!

Let’s get to the fun part. We are going to create bubbles by clicking on the canvas. To achieve this, we'll start by creating a click event handler:

```js
canvas.addEventListener('click', handleDrawCircle);
```

Since we need to know where we clicked on our canvas, we are going to keep track of it in our `handleDrawCircle` function and use the event’s coordinates -

```js

//We are adding x and y here because we will need it later.
let x, y
const handleDrawCircle = (event) => {
  x = event.pageX;
  y = event.pageY;

  // Draw a bubble!
  drawCircle(x, y);
};
```

### Drawing circles using arc method

To create circles, we are going to utilize the [`arc` method available on canvas’s context.](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc) `Arc` method accepts `x` and `y` - the center of the circle, a radius and a start angle and end angle which for us will be `0` and `2* Math.PI` since we are creating a full circle.

```js
const drawCircle = (x, y) => {
  context.beginPath();
  context.arc(x, y, 50, 0, 2 * Math.PI);

  context.strokeStyle = 'white';
  context.stroke();
};
```


![Drawing circles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zrbq3gcpff40nbvzbt8.png)


### Moving circles using requestAnimationFrame method

Now that we have circles, let's make them move because....

![A scene from the "I like to move it" song from the movie -Madagascar](https://media.giphy.com/media/ptS6CV6Ty7Dt26k6wq/giphy.gif)

Remember, that when we created circle, we used the `arc` method which accepted `x` and `y` coordinates - the center of the circle. If we move the `x` and `y` coordinate of our circle really fast, it will give an impression that the circles are moving. Let’s try that!

```js
//Define a speed by which to increment to the x and y coordinates

const dx = Math.random() * 3;
const dy = Math.random() * 7;

//Incremenet the center of the circle with this speed
x = x + dx;
y = y - dy;
```

We can move this inside a function -

```js
let x, y;

const move = () => {
  const dx = Math.random() * 3;
  const dy = Math.random() * 7;

  x = x + dx;
  y = y - dy;
};
```

To give our circle a seemless movement, we are going to create an `animate` function and use browser's `requestAnimationFrame` method to create a moving circle.

```js
const animate = () => {
  context.clearRect(0, 0, canvas.width, canvas.height);

  move();
	drawCircle(x,y);

  requestAnimationFrame(animate);
};

//Don't forget to call animate at the bottom 
animate();
```


![Circle animating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z01uy7e82sqia06svu5t.gif)



### Creating Particles: Introducing the Particle Class

Now that we have created one circle, it’s time to create multiple circles!

But before we create multiple circles, let's prepare our code.
 In order to avoid repeating our code, we are going to use classes and introduce Particle class. Particles are the building blocks of our dynamic artwork and animation. Each bubble is a particle with its own position, size, movement, and color attributes. Let's define a **`Particle`** class to encapsulate these properties:

```js
class Particle {
  constructor(x = 0, y = 0) {}

  draw() {
    // Drawing the particle as a colored circle
    // ...
  }

  move() {
    // Implementing particle movement
    // ...
  }
}
```

Let’s move some of the constants we had set up to the Particle class -

```js
class Particle {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
    this.radius = Math.random() * 50;
    this.dx = Math.random() * 3;
    this.dy = Math.random() * 7;
  }

  draw() {
    // Drawing the particle as a colored circle
    // ...
  }

  move() {
    // Implementing particle movement
    // ...
  }
}
```

The **`draw`** method will be responsible for rendering the particle on the canvas. We have already implemented this functionality in `drawCircle`, so let’s move it in our class and update variables to be class variables

```js
class Particle {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
    this.radius = Math.random() * 50;
    this.dx = Math.random() * 3;
    this.dy = Math.random() * 7;
    this.color = 'white';
  }

  draw() {
    context.beginPath();
    context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
    context.strokeStyle = this.color;
    context.stroke();

    context.fillStyle = this.color;
    context.fill();
  }

  move() {}
}
```

Similarly, let’s move the move function within the class-

```js
move() {
	this.x = this.x + this.dx;
	this.y = this.y - this.dy;
}
```

Now, we need to make sure that in our event handler, we are calling the class Particle. 


```js
const handleDrawCircle = (event) => {
  const x = event.pageX;
  const y = event.pageY;

  const particle = new Particle(x, y);
};

canvas.addEventListener('click', handleDrawCircle);
```

Since we need to access this particle in our animate function in order to call the `move` method on it, we will store this particle in an array called `particleArray`. This array will also be helpful when create lots of particles. Here’s the updated code to reflect this -

```js
const particleArray = [];

const handleDrawCircle = (event) => {
  const x = event.pageX;
  const y = event.pageY;

  const particle = new Particle(x, y);
  particleArray.push(particle);
};

canvas.addEventListener('click', handleDrawCircle);
```

Remember to update the `animate` function too - 

At this point, you will see this particle on your screen-

![Multiple circles moving](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92rs6a5i1xn4v2gzdv8o.gif)


Awesome! Now, to the fun part! Let's creates lots of circles and style them to make them look like bubbles. 

To create lots of bubbles, we are going to create particles using `for` loop and add them to the `particleArray` we had created here. 

```js
const handleDrawCircle = (event) => {
  const x = event.pageX;
  const y = event.pageY;

  for (let i = 0; i < 50; i++) {
    const particle = new Particle(x, y);
    particleArray.push(particle);
  }
};

canvas.addEventListener('click', handleDrawCircle);
```


In the animate function, we'll continuously update the canvas by clearing it and redrawing the particles in their new positions. This will give an illusion of the circle moving.

```js
const animate = () => {
  context.clearRect(0, 0, canvas.width, canvas.height);

  particleArray.forEach((particle) => {
    particle?.move();
    particle?.draw();
  });

  requestAnimationFrame(animate);
};

animate();
```

![Multiple circles animating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ya5u8w8qetqkcy2nbowe.gif)




Now that we have bubbles moving, it’s time to add color to them to make them look like bubbles!

We will do this by adding a gradient fill to bubbles. This can be done using the `context.createRadialGradient` method.

```jsx
const gradient = context.createRadialGradient(
  this.x,
  this.y,
  1,
  this.x + 0.5,
  this.y + 0.5,
  this.radius
);

gradient.addColorStop(0.3, 'rgba(255, 255, 255, 0.3)');
gradient.addColorStop(0.95, '#e7feff');

context.fillStyle = gradient;
```

![Bubbles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l80yde62l9mw3ceh1f1i.gif)


[Here’s the final codepen](https://codepen.io/shrutikapoor08/pen/wvQXMVO
) if you want to take a look at the source code


### Wrap Up

Congratulations! You've just created something super fun using only HTML Canvas and JavaScript. You've learned how to use the `arc` method, leverage `requestAnimationFrame`, harness the power of JavaScript classes, and style your bubbles using gradients for the 3D bubble effect.

Feel free to experiment with colors, speeds, and sizes to make your animations truly unique.

I hope you had as much fun following this tutorial as I did creating it. Now, it's your turn to experiment. I would love to see if you tried this out and what you created. Share with me your code link and I would love to check it out.

--- 
And now a #DevJoke - 

Question - Who won the debate for the best name for loop variable?

Answer - i won. 

---

If you enjoyed this article, share it with someone who will benefit from it.

If you are interested in articles like this and front-end articles on JavaScript, React, GraphQL or Accessibility and career advice from a. Staff Engineer, [sign up for my newsletter](https://tinyletter.com/shrutikapoor) and get these directly in your inbox.