Filterly

Un produit de transformation de photos par IA — dix familles de filtres, des nouvelles coiffures aux portraits d’animaux en passant par les photos professionnelles et les versions dessin animé — livré en applications iOS et Android, avec un front-end web devant. C’est ce front-end web que j’ai développé, et il est bâti pour que le trafic ne coûte presque rien : un site Next.js en App Router rendu presque entièrement côté serveur, prérendu et revalidé sur une fenêtre de cinq minutes, servi depuis l’edge de Vercel — dix visiteurs et dix mille visiteurs y créent le même travail.
Résultats
- Fenêtre de revalidation — la plupart des visites sont un succès de cache, pas un rendu
- 300 sFenêtre de revalidation — la plupart des visites sont un succès de cache, pas un rendu
- Cartes de filtres rendues côté serveur, livrées en HTML
- 10Cartes de filtres rendues côté serveur, livrées en HTML
- Fichier de police dans le chemin critique, auto-hébergé
- 1Fichier de police dans le chemin critique, auto-hébergé
- Hôte tiers que le premier rendu attend
- 0Hôte tiers que le premier rendu attend
Observé depuis le site en ligne en août 2026 — le code appartient au client : ces chiffres sont mesurés sur ce que sert filterly.app, et non lus dans un dépôt.
Conçu pour encaisser une campagne
Des composants serveur par défaut
La bannière, les dix cartes de filtres et l’appel à l’action final sont rendus côté serveur ; seuls quelques îlots interactifs partent en JavaScript.
Prérendu, puis mis en cache à l’edge
Un prérendu statique sur Vercel revalidé toutes les cinq minutes : une vague de visiteurs partage une seule page en cache, et un seul d’entre eux déclenche un rendu.
Un routage par langue d’abord
Chaque route vit sous app/[locale], la langue retenue est conservée dans un cookie NEXT_LOCALE, et les autres langues redirigent au lieu de renvoyer un 404.
Le texte comme catalogue
Les cartes portent des clés de traduction — newHairstyles, faceAnalysis, studioModelling — plutôt que du texte écrit dans le balisage.
Rien qu’elle ne dessine
Aucune bibliothèque d’icônes : chaque carte porte sa propre icône de 24 px en ligne, la page ne livre donc que les tracés qu’elle affiche.
Une police, chargée en local
Une seule police via next/font, préchargée en un woff2 — le premier rendu n’attend aucun hôte tiers.
Une traduction, pas une seconde page
Chaque langue est sa propre URL mise en cache sous app/[locale] : ajouter une langue ajoute un fichier de messages, pas une page à maintenir.
Comment ça marche
Filterly transforme un selfie en autre chose. Dix catégories couvrent l’ensemble — New Hairstyles, Face Analysis, Photo Editing, Professional Look, Studio Modelling, New Outfits, Pet Portraits, Cartoon Filters, Celebrity Styles et Creative Transforms — et le produit lui-même se livre en application native sur l’App Store et Google Play. Le versant web tient en deux propriétés : filterly.app, la porte d’entrée, et get.filterly.app, l’application une fois connecté. Mon travail, c’était le front-end web, en Next.js et React.
Le problème de montée en charge d’une telle porte d’entrée n’est pas celui d’un back-end. Personne ne l’interroge, rien n’y est écrit — mais son trafic arrive par vagues, depuis des campagnes payantes, essentiellement sur des téléphones en données mobiles, et une page rendue à chaque visiteur transforme une bonne journée de publicité en incident d’infrastructure. Toute la conception répond donc à une seule question : quel travail crée un visiteur de plus ? La réponse visée est : aucun.
Cela commence par rendre la page une fois plutôt qu’à chaque requête. Elle est prérendue statiquement et revalidée sur une fenêtre de cinq minutes : une vague de visiteurs reçoit le même HTML mis en cache depuis le nœud edge le plus proche, et un seul d’entre eux déclenche un nouveau rendu en arrière-plan. La langue, elle, est résolue dans l’URL plutôt qu’au moment de la requête — chaque route vit sous un segment app/[locale], la langue retenue est écrite dans un cookie NEXT_LOCALE, et une requête vers une autre langue est redirigée vers son propre chemin au lieu de renvoyer un 404 — ce qui garde chaque langue comme une page mise en cache à part, et non comme une branche à exécuter.
La seconde moitié, c’est ce qu’on demande au navigateur de télécharger. La page est construite comme l’App Router demande à l’être, et non comme on écrit d’ordinaire une page de présentation : la bannière, la grille des dix cartes de filtres et l’appel à l’action final sont tous des composants serveur React, rendus côté serveur et diffusés dans le flux, ne laissant qu’une poignée d’îlots clients — le fond animé, le déroulé « comment ça marche » — à livrer en JavaScript. Ce qui atteint le navigateur, c’est la minorité interactive de la page, pas la page entière. Les dix cartes portent des clés de contenu traduit — newHairstyles, faceAnalysis, studioModelling — si bien que les textes forment un catalogue de messages, et qu’une nouvelle langue est un fichier plutôt qu’une seconde page à maintenir.
Le reste, ce sont les petites choses qui décident de la façon dont un téléphone sur un mauvais réseau vit tout ce qui précède. La surface, c’est Tailwind sur un noir profond, les gris fixés en valeurs arbitraires (cartes #111, bordures #1a1a1a, texte #888) plutôt que passés par un thème ; chacune des dix cartes dessine sa propre icône de 24 pixels en ligne au lieu d’importer une bibliothèque d’icônes pour en dessiner dix ; et l’unique police est chargée localement via next/font et préchargée en un seul woff2, si bien que le premier rendu n’attend rien d’un hôte tiers. Aucune de ces décisions n’est brillante prise isolément. Ensemble, elles font la différence entre une page qui survit à sa propre publicité et une page qui ne fonctionne que lorsque personne ne la regarde.