gaearon / overreacted.io

Personal blog by Dan Abramov.
https://overreacted.io
7.04k stars 1.7k forks source link

Need help: compressing images #804

Open gaearon opened 8 months ago

gaearon commented 8 months ago

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.

plutoniumm commented 8 months ago

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

gaearon commented 8 months ago

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

SidKH commented 8 months ago

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).

sonigeez commented 8 months ago

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

leerob commented 8 months ago

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 />;
}
QuantGeekDev commented 8 months ago

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

gaearon commented 8 months ago

Sure webm sounds good.

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

QuantGeekDev commented 8 months ago

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

QuantGeekDev commented 8 months ago

@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

QuantGeekDev commented 8 months ago

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

QuantGeekDev commented 8 months ago

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.

QuantGeekDev commented 8 months ago

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?

QuantGeekDev commented 7 months ago

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

QuantGeekDev commented 7 months ago

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

QuantGeekDev commented 7 months ago

@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