On the last page of Matt Nelson's seminal work "#WeRateDogs: The Most Hilarious and Adorable Pups You've Ever Seen", you'll find a definition for the word "zoom":
zoom /zoom/ noun
- A very speedy move done by a dog. Incredibly hard to document, but universally recognized as a thing that happens. Appears to break laws of physics, but only because when your dog does something average, you think it is the greatest thing ever.
Most dog owners (and even owners of some ambitious cats) are familiar with these "zooms", also referred to as "zoomies". For those of you who aren't, let me point you in the direction of this instructional video from my dog, Jpeg.
A few months after I joined &yet, I wanted to give our flagship product Talky a case of the zoomies. Here's how I did it.
The Fastest JavaScript is JavaScript You Never Run
Like almost every website built since 2016, Talky uses React, specifically a slightly modified version of Create React App. Because &yet is a forward-thinking organization, Talky is also #serverless, so everything renders in the browser. However, our efforts to ensure that every browser ships with an embedded version of Talky have been largely unsuccessful, so it takes several seconds for all of Talky's bespoke code to be downloaded over The Internet. This means that for a few terrifying moments, browsers display a blank page, giving our users a chance to consider their life choices that have brought them to this harrowing moment.
As it turns out, there's a way to reduce the length of time this blank page displays, and that's by eliminating as much JavaScript as possible. Before embarking on this JavaScript extermination-a-thon, I ran a very scientific benchmark to give myself an idea of the kind of performance increase I could expect.
Test | Ops/sec | |
---|---|---|
Code | console.log("This runs some code."); |
68,499 (±3.00%), 100% slower |
No Code | // This doesn't run any code! |
746,370,478 (±1.32%), fastest |
As you can see from this exhaustive experimentation, the fastest JavaScript code is code you never run. With these results in hand, I embarked on a journey to ship as little JavaScript as possible.
Shaving Megabytes is Easier Than Shaving Yaks
The first thing I wanted to do was identify easy opportunities to drop or replace third-party libraries that contributed significantly to our main bundle size (which was about 2 MB when I began). Since we're using a custom version of Create React App, it was easy to add in Webpack's Bundle Analyzer. Our first analysis wasn't very promising, reporting that the three largest libraries were talky-core
, react-dom
, and react-emojione
, all three of which are absolutely essential to optimal Talky performance. After sharing my findings with Talky's Number One Fan Lance, he gave me what would end up being the best bit of performance advice:
Honestly, making the home page just a static file would be more useful.
Despite the complex nature of Talky, the homepage is actually the same across refreshes, making it a perfect candidate for staticification. There was only one issue though: how would we build the homepage in such a way that it knew about the main Talky assets so that we could prefetch them?
Yet again, the fact that we're using a custom build of Create React App made this easy. By using Webpack's HtmlWebpackPlugin
, we are able to output multiple HTML files, one of which contains our static homepage, and the other that contains the original output of Create React App. For maximum performance we inline all the styles and JavaScript necessary for the homepage. Since we know the output of Create React App, we're able to also utilize the prefetch
directive to begin downloading the main bundle necessary for Talky, meaning that by the time a user clicks on "Start a chat", their browser already has all the Talky code downloaded!
Gotta Go Fast
When we shipped the new static version of Talky's homepage, we saw a ten-fold decrease in page load time. Our first meaningful paint is now just barely over one second, down from almost 12 seconds prior to the changes. The changes were immediately apparent to other members of our team, but I still wasn't satisfied. While we were able to bring our main bundle size down to 265 KB gzipped (down from 358 KB), I knew that we could shave off another 30 KB or so by switching from React to Preact, a library that offers a similar API to React at under 3 KB (!!!). Switching was as easy as defining an alias from React
to Preact
in Webpack. While most things worked (including joining a room and seeing video feeds), others broke in unexpected ways (particularly the chat input). Since we use Redux for state management, it wasn't exactly straightfoward to debug, so we've tabled the switch from React to Preact for another day.
While most of our speed optimizations focused on the homepage, we're still thinking about how we can improve the speed (and perceived speed) of the main Talky application. We have some really clever ideas, and can't wait for you to see (and feel!) the results.
Stay zoomy my friends!