320kbps.club : construire un site que je suis seul à vouloir

320 kbps (kilobits par seconde), c'est la qualité maximale pour les fichiers audio compressés.

320kbps.club : construire un site que je suis seul à vouloir

Il y a des projets qu'on lance parce qu'un marché existe. Et il y a ceux qu'on lance parce qu'on n'en peut plus de perdre des morceaux dans un fil Discogs, une note de téléphone et sept playlists mal nommées. 320kbps.club appartient à la seconde catégorie.

Le principe tient en une phrase : j'écoute, je choisis, je publie. Pas d'algorithme, pas de recommandation automatique, pas de bouton « suivre ». Un morceau qui apparaît sur le site, c'est un morceau que j'ai vraiment écouté et que j'ai jugé assez bon pour occuper un écran entier chez quelqu'un d'autre.

J'écoute et apprécie des titres sur plusieurs plateformes, j'y fais des playlists, qui ne se synchronisent pas entre elles. Et quand j'ai envie de partager simplement un coup de coeur ... c'est là que ça devient compliqué pour rien.

TL;DR: j'ai fait un Linktree musical.

Pas un site qui diffuse, pas une destination d'écoute de plus, juste un endroit où dire : j'aime ce morceau. Peu importe où vous l'écoutez, vous devez pouvoir l'emporter là où vous voulez, Spotify, Apple Music, Deezer, Qobuz, YouTube Music, SoundCloud. Le site ne joue rien lui-même. Il pointe.

C'est un postulat presque trivial, mais il change tout : la valeur n'est jamais dans la plateforme de destination, elle est dans le geste qui précède, quelqu'un a écouté, quelqu'un a jugé que ça valait le coup, quelqu'un vous l'envoie. Le reste, streaming compris, n'est qu'un détail de tuyauterie.

Le site n'a qu'un seul utilisateur avec un pouvoir d'édition. Tout le reste est public, mais rien n'est collaboratif.
It's not a bug, it's a feature.

Vendredi soir, une intuition. Dimanche, un site en ligne. Entre les deux : des choix, des reculs, et au moins un truc qui m'a fait douter de ma propre lecture des logs.

320kbps.club — hand-curated music discovery
A hand-curated collection of tracks discovered by Florent, with factual context and editorial writing.

Le MVP : aller vite, mal, mais vrai

La première version tenait sur une intuition simple : un flux d'accueil, une fiche par morceau, une recherche, des likes, et même un système de comptes pour de futurs lecteurs. Le tout construit en quelques heures. Poussé en ligne tel quel, pas peaufiné : voir ce que ça donne en vrai plutôt que sur le papier.

Beaucoup de fonctionnalités. Trop, en fait.

C'est le syndrome classique du MVP qui copie la forme d'un produit existant sans se demander si cette forme sert le projet. J'avais construit un mini-Spotify communautaire alors que je voulais une vitrine personnelle.

Un comble, pour un site censé se limiter à mon seul jugement. Les comptes lecteurs, les likes, les tags : autant de mécaniques pensées pour une audience qui interagit. La vraie question du produit était ailleurs, et je ne l'avais pas encore posée correctement : qu'est-ce qui donne envie de rester trente secondes de plus sur un morceau qu'on ne connaissait pas ?

Le pivot que je n'ai pas fait

En cherchant comment faire grandir ce premier jet, l'idée a émergé de transformer le site en plateforme multi-utilisateurs. Chacun son compte, sa curation, son abonnement. Un SaaS de curation musicale, en somme.

L'idée s'est effondrée en moins d'un échange. Un site qui vaut par la rigueur d'un seul jugement humain devient un produit générique dès qu'on ouvre les vannes à tout le monde.

La valeur de 320kbps.club, ce n'est pas la technologie de recommandation. C'est le fait qu'une personne précise ait dit oui à ce morceau précis.

Multiplier les curateurs, c'est refaire Spotify avec plus d'étapes.

J'ai gardé l'idée dans un coin de tête le temps d'un café, je l'ai laissée refroidir, et je l'ai abandonnée sans regret.

Ce genre de non-décision compte autant qu'une fonctionnalité livrée. Elle m'a évité de passer des jours à construire un produit que je n'aurais pas eu envie d'utiliser moi-même.

Simplifier au lieu d'empiler

Le pivot écarté, restait à reprendre le premier jet et à le corriger dans l'autre sens : resserrer, pas grandir.

Une fois le périmètre coupé (pas de comptes lecteurs, pas de likes, pas de tags collaboratifs), une bonne partie de la base technique posée la veille ne servait déjà plus à rien. J'ai retiré des couches entières que plus personne n'utilisait, et je suis revenu à des requêtes écrites à la main plutôt qu'à des outils générés automatiquement.

Le gain n'est pas seulement technique. C'est une question de lisibilité : je préfère voir exactement ce que fait le code, sans deviner ce qu'un outil a décidé de faire à ma place.

Retirer les comptes lecteurs a eu un effet en cascade sain : plus besoin de gérer des sessions, des e-mails, des flux d'inscription. Un site à un seul éditeur n'a pas besoin de l'infrastructure d'un site à dix mille comptes.

Trouver une identité visuelle : les slides plein écran

La version « liste avec pagination » fonctionnait, mais elle ressemblait à n'importe quel blog musical. Ça marchait, mais ça ne donnait envie de rien.

Le vrai tournant esthétique est venu d'une question simple, posée en regardant l'écran : et si chaque morceau occupait l'écran entier, comme une pochette qu'on regarde vraiment, plutôt qu'une ligne dans un tableau ?

De là est née l'architecture actuelle : un défilement plein écran, une image par morceau, un fond dont la couleur est extraite automatiquement de la pochette (calculée une seule fois, jamais recalculée à l'affichage, pour ne pas payer ce coût à chaque visite), un contraste de texte choisi automatiquement selon la luminosité du fond.

Le défilement déclenche (après clic sur l'icône qui va bien) la lecture d'un extrait audio de trente secondes du morceau visible à l'écran, en fondu, avec un bouton muet par défaut.

Une pochette qu'on doit chercher dans une grille n'a jamais la même force qu'une pochette qui occupe tout l'écran.

Cette identité visuelle s'est ensuite propagée partout : la page « about » est devenue un manifeste en chapitres colorés, chaque univers thématique (playlist) reprend le même principe.

Une règle que je me suis imposée strictement en cours de route : l'en-tête ne change jamais d'icône selon la page. Même bouton, même emplacement, même comportement, sur toutes les pages publiques.

La seule exception tolérée est un état visuel (le bouton son grisé quand rien ne joue), jamais un changement de fonction. Ce genre de règle semble mineure sur le papier. En pratique, c'est ce qui évite qu'un visiteur reconstruise mentalement l'interface à chaque page.

Automatiser la corvée

Ajouter un morceau à la main, pochette par pochette, genre par genre, aurait tué le projet par lassitude.

L'essentiel du travail de complétion est donc automatisé :

  • Recherche de la pochette, de l'extrait audio, du genre, de l'album, de l'année et du label à partir de bases publiques ...
  • Import possible depuis une simple capture d'écran d'une playlist externe, dont le texte est extrait automatiquement 
  • Détection des quasi-doublons pour les signaler plutôt que les rejeter d'office, un faux négatif étant un souci bien plus mineur qu'un faux positif qui écarterait à tort un vrai titre différent

Tout ce pipeline est conçu pour ne jamais écraser un champ déjà renseigné à la main, et pour pouvoir être relancé sans risque : il ne complète que ce qui manque encore.

C'est surtout une histoire de API plutôt que de AI.

Le dernier ajout a été la recherche automatique de la vidéo associée à un morceau, en privilégiant les sources officielles plutôt qu'une recherche texte brute, bien plus sujette aux erreurs (covers, versions live). Sur le papier, une fonctionnalité anodine.

Dans les faits, elle m'a rappelé une règle que j'oublie régulièrement : une fonctionnalité qui échoue en silence est pire qu'une fonctionnalité qui n'existe pas.

La première version du code envoyait bien les requêtes, mais avalait le résultat sans jamais le refléter quelque part. Succès et échec produisaient le même message générique. Résultat, repéré le jour même en relançant le traitement sur tout le catalogue : plus de cent cinquante requêtes envoyées, cent pour cent d'échec, et strictement aucun indice pour comprendre pourquoi.

Il a fallu creuser jusqu'au détail de la réponse pour découvrir le vrai message d'erreur, jusque-là capturé puis jeté, corriger, et relancer.

Un pipeline qui ne dit jamais pourquoi il échoue n'est pas fiable, il est juste silencieux.

Playlists, univers, et la question du classement

Le classement des morceaux a changé plusieurs fois de forme. D'abord des tags, jamais vraiment utilisés, puis retirés entièrement une fois devenus du poids mort. Puis des genres factuels, remontés automatiquement, affichés comme simple repère.

La forme actuelle s'appelle « univers » : des playlists thématiques éditoriales, complétées par deux mécanismes virtuels qui ne sont jamais stockés et se recalculent à la demande :

  • « Random », le fourre-tout de tout morceau non encore affecté à un univers. Une liste synchronisée aurait pu se désynchroniser à chaque création ou déplacement de morceau ; recalculée à chaque appel, elle ne peut tout simplement pas dériver.
  • une playlist par genre, générée à partir des genres déjà renseignés, accessible depuis la fiche d'un morceau comme depuis le listing des artistes.

Ce choix (calculer plutôt que stocker et synchroniser) revient plusieurs fois dans le projet. Chaque fois qu'une donnée peut se dériver de la source de vérité, la dériver coûte moins cher en bugs qu'un système de synchronisation qu'il faudra un jour déboguer à trois heures du matin.

Ce qui casse en prod, et ce que ça apprend

Un projet qui tourne réellement en production produit son lot d'incidents mineurs mais instructifs.

Le premier :

Une image de partage qui ne se met pas à jour après modification d'un morceau ou d'une playlist. Deux causes empilées, pas une seule. D'abord, un problème de mise en cache côté serveur qui gardait l'ancienne image indéfiniment après le premier appel.

Une fois ce point corrigé, le problème persistait : les réseaux sociaux mettent eux-mêmes en cache l'image associée à une adresse, indépendamment de mon propre serveur. Tant que l'adresse ne change pas, ils ne la re-récupèrent jamais, même après une nouvelle tentative.

La solution a été de faire porter à l'adresse elle-même une empreinte dérivée des éléments qui influent sur le rendu (titre, artiste, couleur) : un contenu modifié change l'adresse, donc devient, du point de vue de n'importe quelle plateforme sociale, une ressource neuve à aller chercher.

Le second incident était plus bête et plus révélateur :

À un moment du week-end, deux branches de code ont vécu en parallèle sans que je m'en rende compte, l'une servant de référence par défaut, l'autre accumulant les dernières modifications.

Rien n'était réellement perdu (une fusion faite entre-temps avait déjà rapatrié le contenu), mais la confusion entre deux réglages qui se ressemblent mais n'ont rien à voir l'un avec l'autre aurait pu, dans un scénario légèrement différent, faire tourner une version du site en retard sans le moindre message d'erreur.

Deux réglages qui portent presque le même nom ne sont jamais le même réglage.

Aucun de ces deux incidents n'est spectaculaire. C'est justement ce qui les rend représentatifs du travail réel sur un produit qui tourne : moins de grandes catastrophes que de petits écarts silencieux entre ce qu'on croit que le système fait et ce qu'il fait vraiment.

Où ça en est aujourd'hui

Le site public tient aujourd'hui en quelques surfaces simples : un accueil qui affiche systématiquement les cinquante derniers ajouts avec chargement infini, une fiche par morceau, des univers thématiques (édités à la main ou calculés automatiquement par genre), un index des artistes en simple liste, une page manifeste.

Le travail répétitif (pochette, extrait, genre, album, année, label, vidéo) est automatisé, pour que le seul travail qui reste à faire à la main soit celui qui compte vraiment : décider si un morceau mérite sa place, et écrire pourquoi.

Ce projet ne sera probablement jamais « fini » au sens d'une liste de fonctionnalités cochées. Ce n'est pas un défaut de gestion de projet, c'est la nature d'un site qui documente un goût en train de se former. Il y aura toujours un nouveau morceau à ajouter, une bordure visuelle à ajuster, un bug silencieux à découvrir en production plutôt qu'en test.

C'est précisément pour ça qu'il fallait construire l'outil le plus léger possible pour le faire vivre : moins il y a de friction entre « j'écoute un morceau qui me plaît » et « il est en ligne », plus le projet a des chances de durer.