Skip to main content
Photography of Alvaro Montoro being a doofus
Alvaro Montoro

CSS Aficionado

Firework, by Erwan Hesry on Unsplash

Creating a Firework Effect with CSS

css html webdev tutorial showdev

Last week I created a firework effect with CSS. It is relatively simple (it only requires one HTML element per firework) and customizable (it uses CSS custom properties to customize colors, sizes, positions...)

Here is a demo of the effect (see in full screen):

Before I continue, let me add a couple of disclaimers: first, the following code is a simplified version of the original one, view the demo above for the full customizable code (but it may be a bit complex). And second, this is fun to develop demo, but it may not be the most efficient thing to do code-wise.

How it works

The idea is to have a small element with absolute-sized backgrounds placed in different relative positions (e.g., using percentages). The use of absolute and relative is essential here. Having absolute-sized backgrounds means that their size will not change depending on the size of the container, but their position is relative, so it will change (or give the impression of changing) when the container is resized.

The relative distance between the dots is the same at all times. But as the container grows, the absolute distance between them expands too-similarly to how real fireworks work.

A small inner box contains many circles together, and an outer box contains many circles but spread across the whole area. Four arrows point from some circles from the inner box to the matching circles on the outer box.

Simplified sketch of how the particles work

Look at the circle on the right side pointed by an arrow. Its position is 100% horizontal and 50% vertical. It was the same when the container was little, but it looks like a lot as the container grows. Yet, it is still in the same relative position as before: 100% horizontal and 50% vertical.

The code

After a (short) description of how things will work, let's get our hands dirty with the code! A great way of lea is by doing. Feel free to follow the steps along (you will need to add some things by yourself, which will make your fireworks more unique.)


As mentioned above, the HTML part is straightforward: we just need one element for each firework:

<div class="firework"></div>

I made it a <div> but it could be any other element. If we wanted to make it more accessible, we could add a role of "img" to indicate that it is an image and an aria-label with a short description: "animated cartoon of a firework." But we could argue that it's not needed as it's more presentational and is already empty. So, let's keep it simple for now.


We start by defining the basic styles for our firework: a simple block container absolute positioned on the screen:

.firework {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 0.5vmin;
  aspect-ratio: 1;
      /* background intentionally blank */
  background-size: 0.5vmin 0.5vmin;
  background-repeat: no-repeat;

Let's see the properties and values little by little:

  • position: absolute;: the element will be moved around the screen. Having an absolute position is ideal for that without affecting other elements.
  • top: 50%; left: 50%;: as a default, the firework will be at the absolute center. We can change that later (if you dug into the code already, you saw these changes from firework to firework.)
  • transform: translate(-50%, -50%);: this is important. By translating the element -50% horizontally and vertically, it will grow in all directions (instead of increasing towards the bottom right side.) when we change its size.
  • width: 0.5vmin; aspect-ratio: 1;: the element will be a tiny square. I used the vmin unit, so it's responsive, but you can use px, rem, or your favorite CSS unit.
  • background-size: 0.5vmin 0.5vmin;: this is important too. We set a fixed background size, so once we define the gradients (see below), they will always have the same size, independently of how big the container is.
  • background-repeat: no-repeat;: we don't want the gradients to repeat. Otherwise, we'd have too many, and it wouldn't look nice.

For the background, we are going to add a bunch of radial gradients like this one:

radial-gradient(circle, yellow 0.5vmin, #0000 0) 50% 0%

Note: because the background-size is squared, the keyword circle is not really needed for our demo. I added it in case I had different sized backgrounds.

This gradient will place a circle of size 0.5vmin in the center (50%) top (0%) of the firework element. Now, we will code many other gradients in different positions. Just make sure that you distribute them all across the container. Don't have an empty side while another is full. Try to add equally into all the quadrants (remember that when the container expands, a "sided" firework will look weird.) I added a total of 30 backgrounds (7 per quadrant). As a recommendation, avoid the corners if you can. They are "outliers" and make it look just "meh" when the animation is active.

And talking about animations... Let's define the animation! It will divide into two parts: the firework flying up into the sky (compressed) and the firework explosion.

@keyframes firework {
  0% { 
    transform: translate(-50%, 60vh);
    width: 0.5vmin;
    opacity: 1;
  50% { 
    width: 0.5vmin;
    opacity: 1;
  100% { 
    width: 45vmin; 
    opacity: 0; 

It starts by moving the firework element outside of the bottom of the screen (translate(-50%, 60vh)), and keep it small (width: 0.5vmin) and visible (opacity: 1). The transform specified in the element is translate(-50%, -50%). So the animation will translate the element from the bottom to its original (at 100%).

Halfway through the animation, we are keeping the element small and visible, and from then on, it will expand to its final size (45vmin) and fuse with the background (by setting an opacity of 0).

.firework {
  animation: firework 2s infinite;

Finally, I applied the same styles to the ::before and ::after pseudo-elements of the firework, and then made some minor modifications to both of them (rotating, scaling, using 3D rotation, you pick), so I had triple the number of particles (and in "different positions") without having to add any more backgrounds or elements:

.firework::after {
  content: "";
.firework::before {
  transform: translate(-50%, -50%) rotate(25deg) !important; 
.firework::after {
  transform: translate(-50%, -50%) rotate(-37deg) !important;

The angle of the rotate() is random for both the ::before and ::after. The !important is necessary to override the one from the animation. However, the effect may look cool without it, too. Feel free to try and experiment.

The result

After following these steps, we have a single line of HTML and 50-60 lines of CSS (depending on the number of radial gradients). The result will look like this:

Simpler than the one displayed above, but the code is also considerably simpler. Time to personalize it and make it your own!


As a final touch and to make things easier to edit (but a bit more complicated to follow), I replaced some of the values with some custom properties. The CSS variables make it easier for me to include other fireworks with minimal code changes. I didn't add that part to this article because I wanted to keep it simple, but you can see the variables on the demo's code at the beginning of the article.

I hope you liked the article. If you develop your own version or make changes to the version above, please share it. I'll enjoy checking it and exploring the code :)

Article originally published on