Retour aux projets

Filterly

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.

Architecture

Schéma d’architecture du front-end web de Filterly : un visiteur arrivant sur téléphone reçoit une page prérendue depuis le cache edge de Vercel, sur une fenêtre de revalidation de 300 secondes ; la langue est négociée dans une URL app/[locale] en amont du rendu ; la page elle-même est faite de composants serveur React avec quelques îlots clients, et le schéma sépare ce qui part vers le navigateur de ce qui n’y part jamais, avant de renvoyer vers l’application connectée et vers les applications natives iOS et Android.

La vie d’une requête, de haut en bas, chaque bande répondant à la même question : quel travail crée un visiteur de plus ? Cliquer sur le schéma pour l’ouvrir en taille réelle.

Stack technique

Next.jsReactApp RouterServer ComponentsTailwind CSSISREdge cachingi18nVercel