I recently released Learn WCs and If you’ve seen it, you’ve likely noticed the animation in the background, where the coloured circles move diagonally across the screen. It looks like this:
It works nicely on Chrome and Safari, but I noticed a severe drop in performance on Firefox.
The performance was so bad, that I straight up disabled this animation in Firefox.
How does the animation work?
The animation is built using two nested divs. The outer div is the first child of the site’s body tag.
<div class="background-mask"> <div class="background-gradient"></div> </div> <!-- Rest of content -->
The .background-gradient element is responsible for creating a gradient that spans the entire width and height of its parent container. Like so:
The outer .background-mask is responsible for two things:
- It sets the position to fixed, and makes the container fill the entire dimensions of the viewport.
- Creates a dotted mask over the gradient
This ensures that the colour of the dots is the colour of the gradient directly underneath it:
Here’s the CSS for everything I described above:
.background-mask { --mask-size: 24px; /* Position Styles */ position: fixed; width: 100%; height: 100%; z-index: -1; /* Mask Styles */ mask-image: radial-gradient(black 2px, transparent 2px); mask-size: var(--mask-size) var(--mask-size); mask-position: 0px 0px; animation: mask-move 3s infinite linear; } .background-gradient { background: var(--red); background-image: var(--gradient); width: 100%; height: 100%; } @keyframes mask-move { 0% { mask-position: 0px 0px; } 100% { mask-position: var(--mask-size) var(--mask-size); } } @media (prefers-reduced-motion: reduce) { .hero-background-mask { animation: none; } }
If you’re interested in learning more about masks in CSS, then I can recommend this comprehensive post by Ahmad Shadeed
What’s causing this drop in performance?
Not all CSS properties animate equally. Without going too much into how the browser renders HTML to the page (though I’ve outlined it here), there are a handful of stages it goes through. The three stages that we’re interested in are:
- Layout - When the browser calculates the size and positions of the elements on the page
- Paint - Draws all the visual aspects of the page, like images, colors, shadows, etc
- Composite - Layering the elements on top of one another in the correct order
The order of the pipeline looks like this:
Layout → Paint → Composite
The layout and paint processes can be CPU-intensive, so it’s important to try and reduce the amount of times your CSS triggers the stages in the pipeline*.* The browser helps in some part by optimising performance for certain properties, some skip entire stages of the rendering pipeline and others can leverage hardware acceleration to move computation from the CPU to the GPU.
Animating certain properties, like translate and opacity , both avoids triggering a layout and uses hardware acceleration.
Sadly, this is not the case when animating mask-position. I took a look at Chrome and saw that the paint count for the background div was increasing on every frame. After a few seconds it had already triggered a paint over 1,000 times.
Even with this high paint count, the animation on Chrome feels smooth. However, it feels super janky on Firefox. Annoyingly, I couldn‘t find a way to measure the paint count on Firefox, so any assumptions I make about Firefox’s poor performance is purely conjecture.
What I did notice is that the animation is fine for small devices, but gets worse as the size of the screen increases. My working theory is that Firefox doesn’t batch the layout triggers for each the 24x24 masks, which causes the FPS to tank when more 24x24 masks are present. Again, I might be completely wrong here.
How did I fix this?
Instead of animating badly optimised CSS properties like mask-position , I needed to lean on the more performant properties, like translate.
The solution wasn’t to move the masks by 24px, but to instead move the entire background element using the translate property.
From an abstract standpoint, this is how the animation looks:
Here’s the two line change in the CSS:
/* --mask-size = 24px */ @keyframes mask-move { 0% { transform: translate(calc(var(--mask-size) * -1), calc(var(--mask-size) * -1)); } 100% { transform: translate(0px, 0px); } }
The browser no longer animates the mask-position, which triggered a layout on each frame. Even though the background moves on each frame, through translate it doesn’t trigger a layout or a paint. You can see that the only paints twice, down from 1,000+ every minute.
Eagle-eyed viewers will have spotted a problem. If you remember, the height and width of the background fills the viewport. Shifting the background left and up by 24px leaves us with this empty space in the viewport.
Solving it is as simple as adding the mask size to the width and height of the container:
.background-mask { --mask-size: 24px; width: calc(100% + var(--mask-size)); height: calc(100% + var(--mask-size)); }
Let’s take a look again in Firefox:
It may not be a perfect solution, but it’s always a little satisfying pulling off a fun smoke and mirrors CSS trick.
The above is the detailed content of The Two Lines of CSS That Tanked Performance (fps to ps). For more information, please follow other related articles on the PHP Chinese website!

The end result of Eric Meyer's tutorial on creating this row of slanted images is pretty classy. But it's more about the journey than the destination (there

There actually is a steps() function in CSS, but it's only used for animation. You can't, for example, tell an element it's allowed to grow in height but only

Let's say we're working on the homepage of a news website. You're probably used to seeing some card-based content in a grid layout, right? Here's a classic

A little interview with me over on Uses This. I'll skip the intro since you know who I am, but I'll republish the rest here.

We covered the idea of animating curved text not long ago when a fun New York Times article came out. All I did was peek into how they did it and extract the

Snowpack. Love that name. This is the new thing from the Pika people, who are on to something. It's a bundler alternative, in a sense. It runs over packages

NetNewsWire is one of the classic RSS apps, debuting in 2002. I was pretty stoked when it went 5.0 and was open-sourced in August 2019! You can snag it right

I recently started drawing on my iPad using the Procreate app with Apple Pencil. I’m enjoying the flexibility of drawing this way. What usually keeps me from


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Atom editor mac version download
The most popular open source editor

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

ZendStudio 13.5.1 Mac
Powerful PHP integrated development environment

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function

SecLists
SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.