[{"data":1,"prerenderedAt":227},["ShallowReactive",2],{"work-fr-bereal":3,"work-cards-fr":140},{"id":4,"title":5,"approach":6,"body":62,"brief":69,"card":75,"description":66,"extension":79,"gallery":80,"hero":105,"heroAlt":106,"meta":107,"navigation":108,"outcome":109,"path":113,"problem":114,"reflection":117,"resources":118,"role":126,"seo":135,"status":136,"stem":137,"summary":138,"__hash__":139},"work_fr\u002Fwork\u002Fbereal.md","Recoder BeReal",[7,25,38,50],{"label":8,"title":9,"prose":10,"artifacts":13},"Capture","Les deux caméras, aucune reprise",[11,12],"Le front-end est une PWA pensée d'abord pour le mobile, et son moment décisif est la capture. Quand la notification se déclenche, la caméra avant et la caméra arrière s'ouvrent en même temps : l'arrière montre ce que vous faites, l'avant montre votre réaction, et la publication porte votre localisation. Il n'y a ni filtres ni reprises, parce que la contrainte est tout l'enjeu de BeReal.","J'ai construit cet écran de capture et le flux de soumission qui le sous-tend : ouvrir les deux flux caméra, composer les deux images en une seule publication, et la transmettre à l'API. Le travail intéressant est dans les contraintes, faire en sorte que deux flux caméra simultanés se comportent bien sur un téléphone, et refuser les fonctions (filtres, reprises) que chaque instinct pousse à ajouter.",[14,20],{"src":15,"alt":16,"caption":17,"decision":18,"width":19},"\u002Fimages\u002Fwork\u002Fbereal\u002Fartifact-capture-live.jpg","L'écran de capture en pleine prise sur une interface mobile sombre : la caméra avant remplit le cadre d'un selfie en direct, avec un bouton de déclenchement rond et une commande de basculement de caméra en dessous, sous l'en-tête BeReal et un compte à rebours.","Capture — les deux caméras en direct, sans filtres.","Ouvrir les deux flux caméra en même temps et refuser les reprises est le produit, donc l'écran de capture n'expose qu'un déclencheur et une commande de basculement.","half",{"src":21,"alt":22,"caption":23,"decision":24,"width":19},"\u002Fimages\u002Fwork\u002Fbereal\u002Fartifact-capture-post.jpg","La publication composée prête à envoyer : la photo arrière en plein cadre avec le selfie de la caméra avant en incrustation dans le coin supérieur, et un bouton d'envoi (Envoyer) en dessous.","Composition — cadre arrière, incrustation avant, une publication.","Les deux images se composent en une seule publication avant l'envoi, afin que la double photo soit un objet unique que le reste de l'application peut traiter comme une unité.",{"label":26,"title":27,"prose":28,"artifacts":31},"Pipeline","De la mémoire au CDN, jamais sur le disque",[29,30],"Côté back-end, la photo arrive sous forme de fichier envoyé et doit finir dans un stockage rapide et économique. J'ai construit le pipeline autour du stockage en mémoire de Multer, de sorte qu'une image envoyée est conservée sous forme de buffer plutôt qu'écrite sur le disque du serveur, et diffusée directement vers Cloudinary via un flux d'envoi.","Garder le fichier en mémoire et le diffuser en sortie, c'est aucun fichier temporaire à nettoyer, aucun disque à remplir, et un serveur qui reste stateless. Le endpoint accepte les deux images caméra ensemble (avant et arrière), si bien que la double photo est un objet de première classe dans l'API, et non deux envois sans rapport.",[32],{"src":33,"alt":34,"caption":35,"decision":36,"width":37},"\u002Fimages\u002Fwork\u002Fbereal\u002Fartifact-pipeline.jpg","Un schéma de pipeline en quatre étapes : un envoi multipart de frontCamera et backCamera, conservé en mémoire par Multer sous forme de buffer (marqué sans fichier temporaire, sans disque), diffusé vers Cloudinary avec upload_stream, puis stocké comme URLs sur la publication dans MongoDB.","Le pipeline d'envoi, de la mémoire au CDN.","Conserver l'envoi en mémoire et le diffuser directement vers le CDN garde le serveur stateless, sans fichier temporaire à nettoyer.","wide",{"label":39,"title":40,"prose":41,"artifacts":44},"Transformation","Recadrer par le contenu et par le visage",[42,43],"Le stockage est aussi l'endroit où les images prennent forme, et j'ai laissé le CDN faire le gros du travail. La photo principale est recadrée automatiquement à une hauteur constante, avec une gravité tenant compte du contenu, et les photos de profil utilisent un recadrage tenant compte du visage (la gravité par visage de Cloudinary) en vignettes carrées, pour qu'un portrait reste centré sur le visage au lieu du coin d'une pièce.","Déléguer les transformations à Cloudinary a gardé l'API légère et les résultats cohérents : conversion de format, dimensionnement et recadrage intelligent se font tous à la livraison, pilotés par quelques options de transformation plutôt que par du code de traitement d'image que j'aurais eu à maintenir.",[45],{"src":46,"alt":47,"caption":48,"decision":49,"width":37},"\u002Fimages\u002Fwork\u002Fbereal\u002Fartifact-transform.jpg","Deux blocs d'options d'envoi Cloudinary côte à côte : l'image de publication utilise une gravité tenant compte du contenu auto dans un ratio 3:4, tandis que la photo de profil utilise une gravité tenant compte du visage auto:faces avec un recadrage rond 1:1, les deux lignes de gravité étant mises en évidence.","Deux recadrages, définis par quelques options.","La gravité par contenu pour les publications et la gravité par visage pour les avatars proviennent chacune d'un seul bloc d'options, si bien que le CDN fait le recadrage et l'API reste légère.",{"label":51,"title":52,"prose":53,"artifacts":56},"Tests","Prouver que l'API fonctionne vraiment",[54,55],"Parce que le pipeline est le produit, je l'ai couvert par des tests d'intégration plutôt que de le vérifier à la main. Une suite Jest et Supertest interroge les vrais endpoints (authentification, publications, et le reste) et fait des assertions sur les réponses, de sorte qu'un changement qui casse l'envoi ou la visibilité du fil échoue bruyamment plutôt que silencieusement.","Écrire ces tests est aussi ce qui a fait de moi un relecteur plus attentif dans l'équipe. Une fois que l'API a eu un filet de sécurité, la revue de code a pu se concentrer sur la conception plutôt que sur la chasse aux régressions à l'œil nu.",[57],{"src":58,"alt":59,"caption":60,"decision":61,"width":37},"\u002Fimages\u002Fwork\u002Fbereal\u002Fartifact-tests.jpg","Le résumé du rapport de couverture Jest : 77.42 % des instructions, 68.69 % des branches, 75 % des fonctions et 78.79 % des lignes couvertes, avec un tableau par domaine pour les controllers, middlewares, models et routes.","La couverture sur l'ensemble de l'API, générée par Jest.","Couvrir le pipeline et les endpoints par des tests d'intégration signifie qu'un changement qui casse l'envoi ou la visibilité du fil échoue bruyamment, pas silencieusement.",{"type":63,"value":64,"toc":65},"minimark",[],{"title":66,"searchDepth":67,"depth":67,"links":68},"",2,[],{"role":70,"year":71,"host":72,"scope":73,"shipped":74},"Pipeline d'images (front + back)","2024","HEIG-VD · DévMobil + ArchiOWeb","Équipe de quatre","PWA Vue + API REST Express",{"title":76,"alt":77,"image":78},"Recoder BeReal — une capture à double caméra et un pipeline d'images","Recoder BeReal : la publication composée à double photo à côté du fil d'un ami, sur une interface mobile sombre.","\u002Fimages\u002Fwork\u002Fbereal\u002Fadjacent.jpg","md",[81,85,89,93,97,101],{"src":82,"alt":83,"caption":84},"\u002Fimages\u002Fwork\u002Fbereal\u002Fgallery-feed.jpg","Le fil d'actualité sur une interface mobile sombre : la double photo d'un ami affichée pleine largeur, une prise arrière en paysage avec le selfie en incrustation, sous l'en-tête de l'application et au-dessus d'une barre de navigation inférieure.","Fil — la publication d'un ami, défilement infini.",{"src":86,"alt":87,"caption":88},"\u002Fimages\u002Fwork\u002Fbereal\u002Fgallery-gate.jpg","Le fil flouté derrière une invite centrée indiquant Poste pour voir, qui demande à l'utilisateur de publier son propre BeReal avant de pouvoir voir les publications de ses amis.","Honnête par réciprocité — publie pour voir.",{"src":90,"alt":91,"caption":92},"\u002Fimages\u002Fwork\u002Fbereal\u002Fgallery-comments.jpg","Une publication ouverte sur son écran de détail avec un fil de commentaires en dessous, montrant des réponses et un champ de saisie de commentaire.","Commentaires et réponses sur une publication.",{"src":94,"alt":95,"caption":96},"\u002Fimages\u002Fwork\u002Fbereal\u002Fgallery-profile.jpg","Un profil utilisateur affichant un avatar rond, un pseudonyme, les compteurs de publications, commentaires et amis, et une grille de BeReals récents.","Profil — statistiques et BeReals récents.",{"src":98,"alt":99,"caption":100},"\u002Fimages\u002Fwork\u002Fbereal\u002Fgallery-friends.jpg","L'écran des amis : un champ de recherche et une liste d'amis avec des avatars ronds, plus un onglet pour les demandes d'amis en attente.","Amis et demandes.",{"src":102,"alt":103,"caption":104},"\u002Fimages\u002Fwork\u002Fbereal\u002Fgallery-swagger.jpg","L'interface Swagger UI de l'API REST, listant les groupes de endpoints pour Auth, Users et Publications avec des méthodes GET, POST, PUT, PATCH et DELETE codées par couleur.","L'API REST, documentée avec Swagger.","\u002Fimages\u002Fwork\u002Fbereal\u002Fhero.jpg","Trois écrans du recodage de BeReal sur une interface mobile sombre : la capture en direct à double caméra, la publication composée avec la photo arrière en plein cadre et le selfie de la caméra avant en incrustation, et le fil d'un ami.",{},true,[110,111,112],"Un recodage de BeReal en deux parties qui fonctionne : une PWA Vue pensée d'abord pour le mobile et une API REST Express et Mongoose, avec le flux à double photo opérationnel de bout en bout, de la capture jusqu'au fil d'un ami.","Un pipeline d'images efficace, un envoi en mémoire diffusé vers un CDN avec un recadrage tenant compte du contenu et du visage, et une suite d'intégration Jest et Supertest couvrant l'API sur laquelle il tourne.","La règle du fil honnête intacte : vous ne voyez les publications de vos amis qu'un jour où vous avez vous-même publié, le mécanisme social qui fait de BeReal ce qu'il est.","\u002Fwork\u002Fbereal",[115,116],"Tout le produit BeReal tient en une seule photo honnête : les deux caméras se déclenchent à une notification quotidienne aléatoire, sans filtres, sans reprises. Recoder ça, c'est admettre que la photo n'est pas une fonctionnalité, c'est le produit, et chaque décision technique doit servir cette unique image non filtrée.","Le projet s'est déroulé sur deux cours de la HEIG-VD, une PWA mobile et une API REST, et j'ai pris la partie qui traverse les deux de bout en bout : le pipeline d'images, du moment où les deux caméras se déclenchent jusqu'au moment où la photo stockée et recadrée réapparaît dans le fil d'un ami.","Ce qui m'a le plus marqué, c'est la discipline de couvrir la partie la plus critique par des tests d'intégration, pour que le reste de l'équipe puisse avancer sans craindre de la casser. Au-delà de ça, c'était la première fois que je construisais une application web complète de bout en bout, du front au back, avec Vue, Express et Mongoose. Suivre tout le processus de développement, tests compris, c'est ce qui a fait atterrir la théorie : ça a transformé ce que nous apprenions sur le NoSQL et Express en une vraie application qui fonctionne.",[119,123],{"type":120,"title":121,"url":122},"github","Front-end — Vue mobile PWA","https:\u002F\u002Fgithub.com\u002FHEIG-COMEM\u002FHEIG-VD_DevMobil_REST",{"type":120,"title":124,"url":125},"Back-end — Express REST API","https:\u002F\u002Fgithub.com\u002FHEIG-COMEM\u002FHEIG-VD_ArchiOWeb_REST",{"led":127,"contributed":130,"notTouched":132,"team":134},[128,129],"L'écran de capture à double caméra et tout le flux de soumission côté front-end","Le pipeline d'envoi et de stockage des images côté back-end, ainsi que ses tests d'intégration",[131],"La revue de code et la qualité du code sur l'ensemble de l'API REST",[133],"Le reste des vues front-end ainsi que les systèmes d'amis et de notifications, construits par mes coéquipiers","Équipe de quatre pour les cours DévMobil et ArchiOWeb à la HEIG-VD : Jérémie Zurflüh, Jérémy Martin et Antoine Uldry (développeurs front-end et back-end), et Steve Pasche (design UX\u002FUI).",{"title":5,"description":66},"live","work\u002Fbereal","Un recodage partiel de BeReal mené sur deux cours : une PWA Vue pensée d'abord pour le mobile et une API REST Express. J'ai pris en charge le pipeline de traitement des images de bout en bout, du déclenchement simultané des deux caméras côté front jusqu'à l'envoi, au recadrage et au stockage de la photo côté back.","ly0xgD0oVANB9qWE5CT7GA4CUCyXSzDDO1vbB1O9iSk",[141,147,153,159,161,167,173,179,185,191,197,203,209,215,221],{"path":142,"card":143},"\u002Fwork\u002Fa-ta-dispo",{"title":144,"alt":145,"image":146},"À ta Dispo — mettre en relation les bénévoles d'événements avec les organisateurs","Le logotype dessiné à la main d'À ta Dispo, un logo corail sur une forme d'autocollant rose, sur un fond corail uni.","\u002Fimages\u002Fwork\u002Fa-ta-dispo\u002Fadjacent.jpg",{"path":148,"card":149},"\u002Fwork\u002Fan-aura-of-words",{"title":150,"image":151,"alt":152},"An Aura of Words — les parcs de Lugano, racontés par leurs avis","\u002Fimages\u002Fwork\u002Fan-aura-of-words\u002Fadjacent.jpg","Les cinq parcs de Lugano montrés sur une carte, chacun une aura organique aux couleurs mêlées dimensionnée par le volume d'avis.",{"path":154,"card":155},"\u002Fwork\u002Fbeau-rivage",{"title":156,"alt":157,"image":158},"Beau-Rivage Eco-Redesign — un site d'hôtel de luxe 95 % plus léger","Le site du Beau-Rivage Palace refondu : sobre, élégant, bien plus léger que l'original.","\u002Fimages\u002Fwork\u002Fbeau-rivage\u002Fadjacent.jpg",{"path":113,"card":160},{"title":76,"alt":77,"image":78},{"path":162,"card":163},"\u002Fwork\u002Fbrushbuddy",{"title":164,"image":165,"alt":166},"BrushBuddy — un cube monstre qui guide deux minutes de brossage des dents","\u002Fimages\u002Fwork\u002Fbrushbuddy\u002Fadjacent.jpg","Une main soulève la brosse à dents du socle au sommet du cube BrushBuddy, qui arbore un visage de monstre jaune inquiet.",{"path":168,"card":169},"\u002Fwork\u002Fcultural-trails",{"title":170,"alt":171,"image":172},"Cultural Trails — une app de randonnée patrimoniale full-stack","Deux écrans de l'application Sentiers culturels sur le ruban de sentier vert, pêche et bleu du projet : la carte d'exploration des sentiers le long de la rive de Lausanne, et la navigation en cours de marche suivant un itinéraire sur une carte satellite.","\u002Fimages\u002Fwork\u002Fcultural-trails\u002Fadjacent.jpg",{"path":174,"card":175},"\u002Fwork\u002Fdatabloom",{"title":176,"image":177,"alt":178},"DataBloom — rendre visible l'impact énergétique des usages numériques","\u002Fimages\u002Fwork\u002Fdatabloom\u002Fadjacent.jpg","La fleur DataBloom : des pétales en feutre outremer sur une tige verte segmentée dans un petit pot en terre cuite, sur un fond de studio blanc.",{"path":180,"card":181},"\u002Fwork\u002Felen",{"title":182,"image":183,"alt":184},"ELEN — une caméra spéculative pour les présences sans fil invisibles","\u002Fimages\u002Fwork\u002Felen\u002Fadjacent.jpg","ELEN, une caméra portable au corps orange translucide avec deux poignées blanches, éclairée sur un fond de studio gris, son écran bleu profond visible à travers le plexiglas.",{"path":186,"card":187},"\u002Fwork\u002Ffamily-space",{"title":188,"image":189,"alt":190},"UBS Family Space — l'endroit partagé d'une famille pour gérer l'argent","\u002Fimages\u002Fwork\u002Ffamily-space\u002Fadjacent.jpg","Concept UBS Family Space : deux écrans de téléphone sur un dégradé rouge doux, un hub parent pour les analyses, les limites et les objectifs de l'enfant, et l'écran d'objectif d'épargne de l'enfant.",{"path":192,"card":193},"\u002Fwork\u002Fhuman-loci",{"title":194,"image":195,"alt":196},"Human Loci — un objet d'écoute synesthésique","\u002Fimages\u002Fwork\u002Fhuman-loci\u002Fadjacent.jpg","Human Loci : une boîte d'écoute transparente en acrylique avec trois disques de plâtre pour un parc, un port de plaisance et une gare.",{"path":198,"card":199},"\u002Fwork\u002Fmeeting-pond",{"title":200,"alt":201,"image":202},"Meeting Pond — des objets lumineux connectés pour la présence à distance","Deux unités Meeting Pond connectées posées côte à côte, l'une luisant en bleu et l'autre en rose, chacune montrant des ondes sur une grille de LED 32×32.","\u002Fimages\u002Fwork\u002Fmeeting-pond\u002Fadjacent.jpg",{"path":204,"card":205},"\u002Fwork\u002Fthea",{"title":206,"image":207,"alt":208},"Thea — un nouveau sens, calme, pour vivre avec une allergie","\u002Fimages\u002Fwork\u002Fthea\u002Fadjacent.jpg","Le wearable Thea suspendu dans un entrelacs de cordes orange sur un ciel cobalt, sa jauge oblongue qui luit, un cliché éditorial tiré de la présentation finale.",{"path":210,"card":211},"\u002Fwork\u002Fuefa-female-coaches",{"title":212,"alt":213,"image":214},"Why Not You? — la campagne UEFA pour les entraîneuses","La campagne Why Not You? : une typographie d'affichage percutante sur une entraîneuse, avec la ligne Lead Your Passion, Shape the Game.","\u002Fimages\u002Fwork\u002Fuefa-female-coaches\u002Fadjacent.jpg",{"path":216,"card":217},"\u002Fwork\u002Fwama",{"title":218,"image":219,"alt":220},"Wama — le machine learning comme témoin au bord de l'évier","\u002Fimages\u002Fwork\u002Fwama\u002Fadjacent.jpg","Le montage d'évier construit pour Wama, de biais : un robinet versant dans un bac transparent, le téléphone dans son support découpé au laser affichant le personnage.",{"path":222,"card":223},"\u002Fwork\u002Fwematch",{"title":224,"image":225,"alt":226},"WeMatch — un service de matching AX pour l'univers WeRoad","\u002Fimages\u002Fwork\u002Fwematch\u002Fadjacent.jpg","Carte-titre WeMatch : « WeMatch finds your group before you find your trip » en corail et encre sur un dégradé doux du blanc au rose, avec le personnage rond de l'agent Matchy.",1783535580394]