gaearon/overreacted.io

Need help: compressing images

gaearon opened this issue · 15 comments

I'm gonna run out of bandwidth with these unoptimized images. They used to be optimized when the website was using Gatsby but I lost it during migration. I'd appreciate if someone could run some script to get them all optimized once.

No real need to make them optimized at build time. Just let's do this once for existing images.

This is not a code solution, which will probably also generate multiple sizes for multiple devices, but meanwhile consider ImgBot?

It should do an average of 10-15%-ish for all images based on some XP with it. It runs as in installable github action so its also close to 0 effort

How do I enable it? I think I installed the “app” but not sure what the next step is

SidKH commented

Most bandwidth gets eaten by GIFs, so image optimizations alone would be insignificant.

CleanShot 2024-01-03 at 13 54 24@2x

This GIF alone is 2.5 MB (after lossy compression, the original is 4 MB - almost the size of all png and jpg pictures combined).

deja_vu

And it's from A Complete Guide to useEffect, which I assume is one of the more popular articles.

I fixed similar bandwidth issues on Vercel by converting GIFs to MP4 (2.5MB ≈ 300-400kb).
You'll have to show them in the video component, though, which will require changes to the code and MDX content.

A quick fix alternative to that is to find smaller GIFs or substitute GIFs with still pictures.
Also, adding loading="lazy" to images can help save some bandwidth for users who never scroll to the gifs (Gatsby did this by default).

i would suggest to serve images and gifs with jsdelivr.com that would work as a cdn too.

leerob commented

Another option you can consider is moving GIFs/MP4s into Blob storage.

CleanShot 2024-01-04 at 08 12 15@2x

Haven't tested this but something like:

import { head } from '@vercel/blob';
import Image from 'next/image';

export async function Video({ alt, src }) {
  let { url, contentType } = await head(src);

  if (contentType.startsWith('video/')) {
    return <video src={url} controls />;
  }

  return <Image alt={alt} src={url} unoptimized />;
}

Would you be open to replacing the .gif files with .webm? Why use mp4 when webm offers even smaller filesizes? @gaearon

Sure webm sounds good.

Re: blob, don't want to depend on third party services ideally.

Sure webm sounds good.

Re: blob, don't want to depend on third party services ideally.

Done, PR Open

EDIT: My bad, webm's are a bit broken. Fixing

@gaearon I've been stuck on this and I can't sleep. I've learnt a lot about mdx and SSR in the process, and I'm enjoying it.
The issue I'm experiencing with webm, and which will happen with .mp4 as well:
Gifs behave as images, webm/mp4 behaves as video. This spawned many issues - including short gifs being too low framerate and becoming glitched on conversion to video, which I already solved. But that also means we can't use markdown since it doesn't support video tags. I've managed to recreate the behavior of gifs with videos and autoplaying on mute with controls hidden - but I can't get the videos to load at first. When page loads, I get 404 on the .webm resources in the Network tab, and then they load but it doesn't refresh the page. If I reload the page, the videos are there - and it looks just like a gif. Ironically this happens on the "Full guide to use useEffect" page since it's where most gifs are located. What are the constraints? Your .md files are very clean and I don't want to add complexity. Is it ok if I make a mdx component for the video? React component? Why do you use mdx but save it in .md files?

I've watched your remix conf talk about react from another dimension, and am eating away at the blog posts trying to decipher how to make it work. It's being a pleasant experience so far

I've rewritten parts of the functionality of your blog with a new create-next-app to try and replicate it and I do not get this issue

It seems to be an issue with the way clientside navigation is handled by next js. Here are the request headers:

Here is a failed request header for the web before reload when navigating to the page from home:

GET /interval-wrong.webm HTTP/1.1
Accept: /
Accept-Encoding: identity;q=1, *;q=0
Accept-Language: en-GB,en;q=0.9,en-US;q=0.8,es;q=0.7,ru;q=0.6
Cache-Control: no-cache
Connection: keep-alive
Cookie: Pycharm-8b1dfef3=3de605ef-37d1-4d33-85d1-e4805f71f957; _ga=GA1.1.198835288.1702473753
Host: localhost:3000
Pragma: no-cache
Range: bytes=0-
Referer: http://localhost:3000/
Sec-Fetch-Dest: video
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows""

Here is a request header for the webm after page refresh:

GET /a-complete-guide-to-useeffect/interval-wrong.webm HTTP/1.1
Accept: /
Accept-Encoding: identity;q=1, *;q=0
Accept-Language: en-GB,en;q=0.9,en-US;q=0.8,es;q=0.7,ru;q=0.6
Cache-Control: no-cache
Connection: keep-alive
Cookie: Pycharm-8b1dfef3=3de605ef-37d1-4d33-85d1-e4805f71f957; _ga=GA1.1.198835288.1702473753
Host: localhost:3000
Pragma: no-cache
Range: bytes=0-
Referer: http://localhost:3000/a-complete-guide-to-useeffect/
Sec-Fetch-Dest: video
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"

here is succesful response header for the same webm:
HTTP/1.1 206 Partial Content
Content-Disposition: inline; filename="interval-wrong.webm"
Accept-Ranges: bytes
ETag: "62c018936516c1ef67f778cbb94a6fa4d4b601b9"
Content-Type: video/webm
Date: Thu, 11 Jan 2024 23:15:49 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Range: bytes 0-15403/15404
Content-Length: 15404

Any suggestions? I have tried with relative and absolute paths and something in between.

It works on the preview on Desktop:

https://overreactedio-git-fork-quantgeekdev-feature-medi-71702e-gaearon.vercel.app/a-complete-guide-to-useeffect/
https://overreactedio-git-fork-quantgeekdev-feature-medi-71702e-gaearon.vercel.app/how-are-function-components-different-from-classes/

But it breaks on Safari. Even thought webms are supported on Safari as of 2022. I will get back to this again in the morning - does anyone have any suggestions?

I'm still trying to figure this out. Any input is greatly appreciated

I only just realized that production "overreacted.io" doesn't work on Safari or Chrome on iOS either 😭😭😭 I didn't actually introduce a bug, I just happened to discover it... Let's see if I have what it takes to fix it

EDIT: By "doesn't work" I mean that when you navigate to a page with gifs, such as the guide on useEffect, it won't load the gifs on the first load and will only load them on re-loading the page

@gaearon
Done ✅ . Also fixed the bug for video files not displaying when you navigate from homepage -> post page on Safari

Submitted a new pull request with a cleaner commit history. Hope you find it useful