Playing With Particles Using the Web Animations API

Avatar of Louis Hoebregts
Louis Hoebregts on (Updated on )

When it comes to motion and animations, there is probably nothing I love more than particles. This is why every time I explore new technologies I always end up creating demos with as many particles as I can.

In this post, we’ll make even more particle magic using the Web Animations API to create a firework effect when clicking on a button.

Browser support

At the time I’m writing this article, all major browsers — with the exception of Safari and Internet Explorer — at least partially support the Web Animations API. Safari support can be enabled in the “Experimental Features” developer menu.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
8475No8413.1

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12312412313.4-13.7

If you’re interested in reproducing the Twitter heart animation you could also have a look at this cool article by Ana Tudor which is another great example of exploding particles on a button.

HTML setup

We won’t need much HTML for this demo. We will use a <button> element but it could be another type of tag element. We could even listen to any click on the page to make particles pop from anywhere if we really wanted to.

<button id="button">Click on me</button>

CSS setup

Since every particle has a few CSS properties in common, we can set them in the global CSS of the page. As you can create custom tag elements in HTML, I will use a <particle> tag name to avoid using semantic tags. But truth is, you could animate <p>, <i> or any tag of your choice.

particle {
  border-radius: 50%;
  left: 0;
  pointer-events: none;
  position: fixed;
  top: 0;
}

A couple thing to note here:

  • The particles should not interact with the layout of our page, so we’re setting a fixed position with top and left at 0px each.
  • We’re also removing pointer events to avoid any user interaction on the HTML particles while they are on the screen.

Because styling the button and the page layout is not really the purpose of this article I will leave that on the side.

JavaScript setup

Here are the six steps we will follow in our JavaScript:

  1. Listen to click event on the button
  2. Create 30 <particle> elements and append them into the <body>
  3. Set a random width, height and background for every particle
  4. Animate each particle from the mouse position to a random place as they fade out
  5. Remove the <particle> from the DOM when the animation is complete

Step 1: The click event

// We first check if the browser supports the Web Animations API
if (document.body.animate) {
  // If yes, we add a click listener on our button
  document.querySelector('#button').addEventListener('click', pop);
}

Step 2: The particles

// The pop() function is called on every click
function pop(e) { 
  // Loop to generate 30 particles at once
  for (let i = 0; i < 30; i++) {
    // We pass the mouse coordinates to the createParticle() function
    createParticle(e.clientX, e.clientY);
  }
}
function createParticle(x, y) {
  // Create a custom particle element
  const particle = document.createElement('particle');
  // Append the element into the body
  document.body.appendChild(particle);
}

Step 3: Particle width, height and background

function createParticle (x, y) {
  // [...]
  // Calculate a random size from 5px to 25px
  const size = Math.floor(Math.random() * 20 + 5);
  // Apply the size on each particle
  particle.style.width = `${size}px`;
  particle.style.height = `${size}px`;
  // Generate a random color in a blue/purple palette
  particle.style.background = `hsl(${Math.random() * 90 + 180}, 70%, 60%)`;
}

Step 4: Animate each particle

function createParticle (x, y) {
  // [...]
  // Generate a random x & y destination within a distance of 75px from the mouse
  const destinationX = x + (Math.random() - 0.5) * 2 * 75;
  const destinationY = y + (Math.random() - 0.5) * 2 * 75;

  // Store the animation in a variable because we will need it later
  const animation = particle.animate([
    {
      // Set the origin position of the particle
      // We offset the particle with half its size to center it around the mouse
      transform: `translate(${x - (size / 2)}px, ${y - (size / 2)}px)`,
      opacity: 1
    },
    {
      // We define the final coordinates as the second keyframe
      transform: `translate(${destinationX}px, ${destinationY}px)`,
      opacity: 0
    }
  ], {
    // Set a random duration from 500 to 1500ms
    duration: 500 + Math.random() * 1000,
    easing: 'cubic-bezier(0, .9, .57, 1)',
    // Delay every particle with a random value from 0ms to 200ms
    delay: Math.random() * 200
  });
}

Because we have a random delay, the particles waiting to start their animation are visible on the top-left of the screen. To prevent this, we can set a zero opacity on every particle in our global CSS.

particle {
  /* Same as before */
  opacity: 0;
}

Step 5: Remove particles after the animation completes

It is important to remove the particle elements from the DOM. Since we create 30 new elements on every click, the browser memory can fill up pretty quickly and cause things to get janky. Here’s how we can do that:

function createParticle (x, y) {
  // Same as before
  // When the animation is finished, remove the element from the DOM
  animation.onfinish = () => {
    particle.remove();
  };
}

Final result

Putting everything together gives us what we’re looking for: a colorful explosion of particle goodness.

Not seeing the animation in the demo? Check if your browser supports the Web Animations API. in the support table at the top of the post.

Be creative!

Because all this is using CSS, it’s pretty simple to modify the particle styles. Here are five examples using various shapes… and even characters!


Or hey, we can even explode the button itself like Zach Saucier did in this post.

A button with a gradient exploding into particles