MultiPOVRustWASMAndroidProductionSync audioIA-assisted development

MultiPOV après la PWA : transformer une idée web en système média Rust/WASM + Android

MultiPOV après la PWA

La première version de MultiPOV avait un objectif simple : permettre à des spectateurs de regarder plusieurs points de vue d'une même session Discord ou d'un event, alignés dans le temps, sans compte et sans setup lourd.

Cette PWA est en production sur watchmultipov.app. Elle a validé le produit : YouTube, Twitch et Kick dans une même régie, des sessions partageables, une interface qui ne ressemble pas à un mur de players, et une sync VOD exploitable dans un vrai navigateur.

Mais une fois cette base livrée, la question a changé. Le sujet n'était plus seulement "faire une bonne PWA". C'était : comment transformer cette idée web en système média qui tient la VOD, le live, plusieurs providers, une app native Android et un chemin de release sérieux ?

1. Le point de départ : une PWA déjà en production

La V1 était volontairement pragmatique. Solid, TypeScript, Vite, PWA, hls.js et une configuration de session encodée dans l'URL.

Ce choix a permis de livrer vite sans construire une plateforme trop lourde. Les vidéos et les chats restent fournis par les plateformes. MultiPOV orchestre la régie, la disposition, la timeline et la synchronisation.

Le point important : la PWA n'était pas un prototype jetable. Elle était le premier produit public. C'est précisément parce qu'elle était en production qu'elle a rendu visibles les limites suivantes.

2. La limite du navigateur

Le navigateur suffit pour beaucoup de choses, mais la sync live multi-provider met vite en évidence ses frontières : players iframe, HLS, états de lecture qui ne se comportent pas toujours pareil, latence variable, drift, capacités différentes selon les sources, erreurs qui doivent être normalisées.

En VOD, on peut mesurer un décalage, piloter une horloge et recaler. En live, on dépend davantage du comportement de chaque source et du player qui l'expose. Le produit doit donc séparer deux sujets : mesurer ou initialiser un alignement, puis maintenir une expérience stable avec une clock et des offsets.

C'est là que la PWA seule devenait trop étroite. Il fallait une source de vérité média qui ne soit pas réécrite différemment dans chaque front.

3. Le cœur Rust commun

La première version TypeScript utilisait FFT.js côté client pour la mesure GCC-PHAT. Elle a été utile : elle a prouvé le principe produit et l'expérience utilisateur.

Le cœur actuel est différent. La logique média est remontée dans un moteur Rust commun, exposé en WASM côté web et compilé en natif côté app. L'objectif n'est pas de mettre Rust pour le prestige ; l'objectif est d'avoir un seul endroit où vivent les règles importantes : timing, offsets, états provider, liveness, erreurs et fallbacks.

Cette décision réduit les divergences. Quand une règle de sync change, elle ne doit pas exister en deux variantes incompatibles entre web et Android.

4. La sync : mesurer, maintenir, recoller

La sync n'est pas un bouton magique. Je la traite comme trois opérations distinctes.

D'abord, mesurer. GCC-PHAT permet d'estimer le décalage entre un flux et une référence audio commune. C'est utile pour initialiser une session VOD ou recaler un point de vue.

Ensuite, maintenir. Une clock partagée et des offsets par POV pilotent la lecture : pause, seek, reprise, changement de layout, correction de drift. C'est moins spectaculaire que la corrélation audio, mais c'est ce qui rend l'expérience stable.

Enfin, recoller. Certains événements imposent de reprendre une mesure ou d'appliquer une correction : changement d'état player, source qui repart, live qui dérive, ou interaction utilisateur. Le produit doit accepter ces événements au lieu de prétendre qu'ils n'existent pas.

5. Intégrer YouTube, Twitch et Kick sans publier la recette

MultiPOV intègre YouTube, Twitch et Kick avec une couche provider unifiée. Ce que je veux montrer publiquement, ce n'est pas une collection d'astuces provider par provider. Ce qui a de la valeur, c'est l'architecture.

Chaque provider expose des capacités, des états de liveness, des erreurs et des fallbacks. Ces notions sont centralisées pour que le web et le natif raisonnent avec le même vocabulaire.

6. L'app native : Android comme surface de preuve

L'app native n'est pas une landing page mobile déguisée. Elle fournit une vraie surface Android avec Tauri 2, la régie web embarquée, le cœur Rust commun et des players natifs Media3/ExoPlayer.

Ce choix répond à une contrainte très concrète : pour prouver la sync live, il faut sortir du seul navigateur desktop et mesurer sur device. L'app Android a servi de surface de validation : installation, comportement réel, players natifs, logs, drift, corrections, et stabilité sur une session vivante.

7. Valider sur le web et sur appareil

Le produit web est en production et l'app Android sert de surface de validation complémentaire. Des checks automatisés vérifient les points qui cassent vraiment une version : parcours principaux, assets, liens, configuration et comportement sur appareil. La preuve ne repose donc pas seulement sur l'architecture annoncée, mais sur une expérience réellement exercée dans les deux environnements.

8. Le rôle des agents Claude/Codex

Claude et Codex ne sont pas le produit, mais ils font partie de la méthode. La boucle qui a le plus de valeur est répétable : spécification, implémentation, review, correction, validation.

Sur MultiPOV, j'ai utilisé cette boucle comme un mécanisme de rigueur. Claude aide à produire et transformer vite. Codex sert aussi à relire, challenger, vérifier les invariants, lire les diffs, lancer les checks et forcer une preuve concrète avant de considérer une étape terminée.

La formulation courte serait : spécifié, implémenté, relu avec Claude/Codex, vérifié en production et sur device.

9. Ce que j'en retiens comme développeur produit

MultiPOV est le projet qui montre le mieux ma manière de travailler : partir d'un usage précis, livrer une V1 réelle, puis durcir l'architecture quand le produit le justifie.

La leçon principale est simple : il ne faut pas confondre complexité technique et valeur produit. La sync audio, Rust/WASM, Android ou Media3 ne sont intéressants que parce qu'ils servent une expérience très claire : ouvrir plusieurs POV et les regarder ensemble, proprement synchronisés.

C'est aussi ce que je veux montrer dans ce portfolio : je ne fais pas seulement des écrans. Je peux cadrer un produit, écrire le système qui le rend possible, le mettre en production et le valider dans des conditions réelles sur le web comme sur appareil.