It was the first project I worked on when I (also) came back from a three-month sabbatical. I had previously burned out on coding and I found myself for the first time getting excited about having the freedom to go down rabbit holes to solve even the smallest of problems. Most of these problems weren’t even practical to solve, but I had a good time solving them.
Knowing that the site was going to be a PWA, I wanted to keep the frameworks used as small as possible and take advantage of any tooling that would allow me to do that.
preact-cli seemed like a perfect fit.
face.camp uses the
fetch, and arrow functions (
@babel/present-env, but I noticed that even when supplying the list of supported browsers there was still room to tweak the final build. So I went digging to see what else I could do.
The first thing I wanted to do was found out how many bytes I was saving by shipping ES2015+. Using a script that would build multiple versions of the app with different build configurations, I found that by switching the supported browsers from the default of
['> 0.25%', 'IE >= 9'], I saved 506 bytes. Armed with my slightly over-engineered script, I went out looking for what other configurations would save even the smallest amount.
The next thing I found, is that I could also use native
async/await instead of the
fast-async plugin. This saved me 234 bytes! Sure that’s only a savings of 0.74% of the total bundle size, but…actually I don’t really have a justification for savings 234 bytes. Remember what I said above about doing things that weren’t practical?
I also noticed that some of the babel plugins were using an inline helper to replace the object spread operator. Since the list of supported browsers for the project all supported
Object.assign, I added
useBuiltIns: true to the settings for each plugin. And yes, the savings were even smaller than before (93 bytes, but who’s counting).
When I combined all those build settings I lost some of the aggregate savings, but still came in at 808 bytes smaller than when I started. If you’re as interested as me in shrinking a build by ~2.5%, you can check out the raw gist of the script’s output or try running it yourself.
One of our favorite parts about face.camp is how Slack is able to put the gifs inline into whichever channel or conversation you want. However, we found out that Slack will only do this for images less than 2MB in size. The site uses gif.js and a solution I learned from Philip Roberts’ gifhu.gs to generate the gifs. But they would just often enough come out to greater than 2MB depending on the complexity of the image. I tried lowering the framerate, quality, and size, but found that in order to always get the size lower than 2MB those options noticeable degraded the image quality for most other images.
The easiest solution I found was to make the computer keep re-rendering the gif until the size was under 2MB! It progressively lowers the size of the image and keeps track of the scaling multiplier so any future image in that session will use the new scale. Eagle-eyed users might notice that the progress meter sometimes runs twice, this is why!
In 2018, iOS 11.3 added support for Progressive Web Apps. However, one big part that was missing is support for
getUserMedia inside those PWAs. Since I wanted to keep support for PWAs on Android, I didn’t want to drop support altogether. I ended up stumbling upon the open-source Pinafore project and finding an issue about another shortcoming in iOS PWAs that they had to work around. I ended up copying their solution, which is to use user agent sniffing to remove the manifest.json