Au secours, mes

intro

pourrissent mes perfs

Peu d'histoires sont contées sur Bitmap,

dont le règne en tant que seigneur des images prit fin brutalement

lorsque, tout à coup, les générations futures le rattrapèrent...

Mais les formats ne partent pas sans bruit, et l'histoire se répète,

rendant la gestion des images toujours aussi complexe,

menant des hordes de développeureuses aux portes de l'enfer.

Ainsi les maîtres des lieux avaient tant à faire depuis des lustres,

pour guider ces nombreuses ames, à travers ces différents enfers.

Cette journée allait-elle être semblable à toutes les autres...

... ou pas ?

💀🔥 Journée d'accueil n° 12263 🔥💀

💀🔥 Journée d'accueil n° 12263 🔥💀

  1. 👋 Accueil convivial
  2. 🎤 Mot de bienvenue de la direction
  3. 🚀 Présentation du nouvel organigrame
  4. ❤️ Présentation de nos valeurs
  5. 🪙🪙 Quête
  6. 🥤 Pot de bienvenue

Antoine Charon

CFO | Nocher des stacks

T'as perdu tes 2 pièces ?!!

Nnnrrrraaauuuggghhh....

Mathieu Mort

DRH | La mort incarnée

Il hésitait pour son message de commit entre "Fix bug" ou "Close Issue"...alors forcément j'ai tranché !

Image avec fond blanc
Image déformée
Broken image
Image lourde 11.5 MB
Image floue
Image qui déborde

🔥 Welcome to Hell !!!

18 juillet 1992

99.9%

99.9%

Pages qui requêtent au moins une image.

Source The Web Alamanach 2024

68%

68%

des pages mobiles ont un LCP lié à une image.

Source The Web Alamanach 2024

68%

des pages mobiles ont un LCP lié à une image.

Source The Web Alamanach 2024

Images, images everywhere

Les images ont conquis tout le web. Tout ? non, un village d'irréductibles Sysadmin résistent encore et toujours à l'utilisation d'images sur le web.

Performances

Les images sont au coeurs des performances web, responsables de la plupart des mauvais score de LCP

Pièces

Ne perdez pas vos pièces, elles vous seront utiles pour la fin.

Asphodel

Le royaume des (formats) morts

Comment compresser une image

Genre WEBP

Loading...
Loading...
Loading...
Left comparison image
Right comparison image

Perte et fracas

Les images dans le web sont compressées avec perte.

Illusion d'optique

La qualité est une option qui trompe votre cerveau.

Compression des images

Découpage, Prédiction puis réduction des hautes fréquences.

Format moderne

Ce sont juste des formats qui utilient des Codec Vidéo

Tartarus

Un supplice de tailles

Loading...
Loading...

Scroll down 👇

Achilles
Achilles

Lazy Loading

Code HTML
<img
	src="/megaera.jpg"
	fetchpriority="high" />

<img
	src="/achilles.jpg"
	loading="lazy" />
Achilles

Progressive Image Loading

Code HTML
<img
	src="data:image/webp;base64,UklGRiABAABX..."
	data-src="/achilles.jpg"
	class="progressive" />

<script>
	const image = new Image();

	image.onload = function () {
		document.querySelector('img').src = image.src;
	}

	image.src = document.querySelector('img').dataset.src;
</script>

Le champs des châtiments

L'enfer c'est les autres

Ares

CEO Vénère

Voilà la toute dernière photos d'équipe à mettre sur le HP du site qu'on a reçu de la part de notre photographe

C'est un fichier TIFF

Ares

CEO Vénère

Voilà la toute dernière photos d'équipe à mettre sur le HP du site qu'on a reçu de la part de notre photographe

C'est un fichier TIFF

Dyonysos

Lead Content Designer

Comme promis voici la dernière créa concernant les icônes.

J'ai pas su exporter le format alors je te file le fichier .ai

Transformation

sh
npm install -D sharp
js
const sourcePath = '/home/melinoe/house-of-the-dead/public/img/src/hades-family.tiff';
const outputPath = '/home/melinoe/house-of-the-dead/public/img/out/hades-family-2048.webp';
js
import sharp from 'sharp'
const sourcePath = '/home/melinoe/house-of-the-dead/public/img/src/hades-family.tiff';
const outputPath = '/home/melinoe/house-of-the-dead/public/img/out/hades-family-2048.webp';

await sharp(sourcePath)
js
import sharp from 'sharp'
const sourcePath = '/home/melinoe/house-of-the-dead/public/img/src/hades-family.tiff';
const outputPath = '/home/melinoe/house-of-the-dead/public/img/out/hades-family-2048.webp';

await sharp(sourcePath)
  .resize(2048)
js
import sharp from 'sharp'
const sourcePath = '/home/melinoe/house-of-the-dead/public/img/src/hades-family.tiff';
const outputPath = '/home/melinoe/house-of-the-dead/public/img/out/hades-family-2048.webp';

await sharp(sourcePath)
  .resize(2048)
  .toFormat('webp', { quality: 75 })
js
import sharp from 'sharp'
const sourcePath = '/home/melinoe/house-of-the-dead/public/img/src/hades-family.tiff';
const outputPath = '/home/melinoe/house-of-the-dead/public/img/out/hades-family-2048.webp';

await sharp(sourcePath)
  .resize(2048)
  .toFormat('webp', { quality: 75 })
  .toFile(outputPath);
sh
git commit -am "chore: add all images" && git push

Athena

Staff engineer

Je ne sais pas ce que tu as foutu mais les Ops m'engeulent parce que les blobs ont explosé sur Githuh et les dev mettent 10 minutes à pull main

Git LFS

Sans LFS

┌──────────────────────┐
│ Git Repository       │
│                      │
│ ├── code.js          │
│ ├── styles.css       │
│ ├── image1.png       │ ← 2 MB
│ ├── image2.png       │ ← 3 MB
│ └── image3.png       │ ← 1 MB
│                      │
└──────────────────────┘

      Problème:
  Chaque commit duplique
  les images modifiées
  dans l'historique Git

  ↓

Repo de 250 MB après
   50 commits
❌ Repo lourd et lent
VS

Avec LFS

┌──────────────────────┐    ┌─────────────────┐
│ Git Repository       │    │  LFS Storage    │
│                      │    │                 │
│ ├── code.js          │    │ ├── abc123.bin  │
│ ├── styles.css       │    │ ├── def456.bin  │
│ ├── image1.ptr ──────┼───→│ └── ghi789.bin  │
│ ├── image2.ptr ──────┼───→│                 │
│ └── image3.ptr ──────┼───→│  Objets réels   │
│                      │    │  (téléchargés   │
│  120 bytes/pointer   │    │  à la demande)  │
└──────────────────────┘    └─────────────────┘

  Solution:
Pointeurs légers dans Git
  Objets dans le cloud

  ↓

Repo de 5 MB + 42 MB
    objets LFS
✅ Repo léger et rapide

🔄 Workflow avec Git LFS

1
Installation
git lfs install
2
Configuration
git lfs track "*.png"
3
Usage normal
git add, commit, push
4
Clone rapide
git clone + LFS download
sh
git commit -am "chore: add all images" && git push

...

Hephaïtos

THE sysadmin

Alors t'es gentil mais avec ton dernier commits les runners sont à 500% de CPU

Et la CI met maintenant plus d'une heure à build ton app

Poseidon

Business analyst

Alors après une étude de marché on a décidé de porter l'application sur TVs.

Il faudrait donc supporter des images en 4K maintenant

L'enfer c'est les autres

On doit transformer les essences pour avoir notre site optimisée.

Stockage optimisé

Utiliser Git LFS si le stockage doit être fait dans votre repo

Build long du c**

Ben là, tout est dans le titre...

Elysium

La douceur du nuage

Vous voilà maintenant coincé dans Elysium.

Comme beaucoup d'autres avant vous, vous errez maintenant sans but,

mais peut être que ce paradis n'est qu'une chimère.

Il ne vous reste plus qu'à vous assoir et scroller de vidéo en vidéo...

Un ares et ça repart...

  • Stocker l'essence
  • Bucket S3
  • Accessible en HTTP
  • Transformer au bon format
  • A la volée
  • via des paramètres d'URL
  • https://img.localhell:666/essence.tiff?
    						f=webp&
    						w=1080&
    						q=75
<picture>
  <source type="image/webp" src-set="
  	https://localhell:666/img/heracles.tiff?f=webp&q=75&w=640 640w,
  	https://localhell:666/img/heracles.tiff?f=webp&q=75&w=1024 1024w,
  	https://localhell:666/img/heracles.tiff?f=webp&q=75&w=2048 2048w,
  	https://localhell:666/img/heracles.tiff?f=webp&q=75&w=4096 4096w,
  "/>
  <source type="image/avif" src-set="
  	https://localhell:666/img/heracles.tiff?f=avif&q=75&w=640 640w,
  	https://localhell:666/img/heracles.tiff?f=avif&q=75&w=1024 1024w,
  	https://localhell:666/img/heracles.tiff?f=avif&q=75&w=2048 2048w,
  	https://localhell:666/img/heracles.tiff?f=avif&q=75&w=4096 4096w,
  "/>
</picture>
  • Cache / CDN

Hermes

Plus radin que Charon

C'est bien gentil tout ça mais je peux éviter de dépenser trop d'argent dans les nuages ?

Architecture CMS - Solution d'optimisation d'images

flowchart LR
    User[👤 User/Client]

    subgraph Stack["Docker Compose Stack"]
        direction LR
        Strapi[📦 Strapi
Port 1337] Postgres[(🗄️ PostgreSQL
Port 5432)] LocalStack[☁️ S3
Port 4566] Imgproxy[🖼️ Imgproxy
WebP/AVIF] Varnish[🚀 Varnish
Cache 1 an] end User -->|Admin/API| Strapi User -->|Image Request| Varnish Varnish -.->|Cache HIT| User Varnish -->|Cache MISS| Imgproxy Imgproxy -->|Processed| Varnish Imgproxy -->|Fetch| LocalStack Strapi -->|Upload| LocalStack Strapi -->|Metadata| Postgres style Varnish fill:#f9f,stroke:#333,stroke-width:2px style LocalStack fill:#bbf,stroke:#333,stroke-width:2px style User fill:#9f9,stroke:#333,stroke-width:2px
Cache Varnish 1 an
🖼️ Optimization à la demande
☁️ S3 local (dev)

DEMO TIME - Local

Enfin...

Le format c'est la vie

Nous sommes en 2026, Mort à JPEG ! Vive Webp et AVIF.

La taille ça compte finalement

Le bon format, la bonne taille, la bonne densité, le tout dans des src-set !

L'enfer c'est les autres

...mais des fois c'est pas grave ! Moins de 100 images, on optimize au build et on utilise Git LFS.

L'enfer c'est SURTOUT les autres

Si vous n'avez pas la maitrise des images, ou que vous en avez + de 1000. Déployez une architecture de service image à la demande.

Antoine Caron

Engineering Manager

Engineering Manager

Chez Scaleway

Mathieu Mure

Staff Engineer

Staff Engineer

à Disneyland Paris

Merci

QR code vers les slides

Slides

https://github.com/mathieumure/talk-images-enfer

QR code vers le feedback

Feedback

https://openfeedback.io/nIlFquxGUZ1IJ1cDkc1z/2026-02-13/cmgah8mo800fj1elbs7lmahsn

Pour aller

trop loin