How to optimize First Contentful Paint using GPU-only CSS animations.
## Before

## After

To understand the optimisations I did, we first need to look at the basics of how HTML, CSS and JS areloaded in a browser. I recommend this article. There are tons of articles that explain how the bowser renders a typical website.
Quick understanding of the browser rendering:
- Browsers PAINTS the HTML as it parses
- CSS is first fetched (if you have a link tag for your CSS file)
- During the fetching of the CSS file, the browser doesn’t stop the PAINT process.
- Once the CSS file is fetched the browser blocks the PAINT to first parse the fetched CSS file and then creates a CSSOM (Which is just like the DOM but only for CSS classes)
- Once the browser hits a “script” tag, it blocks the PAINT process to first fetch the JS file and then keeps it blocked till it’s parsed
What we can learn from it?
Well, in our current day applications, the entire page is rendered vai JS (let it be React, angular etc). So our first contenful paint is blocked till the JS file is loaded and parsed.
So can I show something on my SPA (Single Page Application) before my JS is parsed? Well, yes. You can just add an inline style in the index.html file and paint a basic HTML to bring down the FCP and remove it once the JS is loaded. But we would wanna do something fancy with it right? Like show some animations, maybe?
Now this is where things get a bit interesting, when you put an animation like shimmer, the browser will have to be running the PAINT cycle to render these animations. But we know that while our JS file is being fetched and loaded, the PAINT will be blocked. This will lead to frame drops and janky animation.What to do now? I know, you must be thinking that I’ll defer and async my JS file. While might be correct to do so, but there is another way, and that’s what I’ll be talking about now.
GPU only animations
Well nowadays, browsers have become pretty smart. So smart that they can look ahead into the CSS and delegate the PAINT process to GPU only. That way our main thread would be busy fetching the script file and parsing it, while our GPU can still paint the animating frames. One caveat is that the browser still needs to be able to push the frames to be created to the GPU. Not every CSS animation can be pushed to the GPU by the browser. I recommend you read this article about the topic.
One thing to remember here, is that GPUs are great at doing matrix multiplications, so if you set up your animation using a transform, the browser will very easily delegate the PAINT process to GPU, and you’ll end up with jank free 60fps animating loading screens.
And Voila, you have successfully reduced your FCP.
Recap -
- Add some basic CSS needed for the loading screen in the index.html page itself. Instead of manually writing the CSS in html, you just write a css file and then use webpack to push it inside your index.html file.

_inline.scss is my css file which contains only styles for the loading page, and using this webpack rule, I can add these styles directly in my index.html file.
- Try using transform only animations

Again, refer to the article I linked above on GPU animation will give you good info about what animations to use and what to avoid for GPU only animations.
- Don’t forget to remove the loading tags once the JS is loaded (lol)

Result
Before -

Now -
