Comment utiliser Face Motion pour interagir avec la typographie
Publié: 2022-03-10Les concepteurs de sites Web sont toujours à la recherche de nouvelles façons d'améliorer la présentation du contenu d'une page. Parfois, cela peut conduire à des solutions ingénieuses ou à interagir avec des technologies souvent tenues à l'écart du domaine du design. Dans cet article, nous allons mettre la typographie en contact avec l'intelligence artificielle, en utilisant l'apprentissage automatique pour détecter des éléments tels que la proximité du visage de l'utilisateur afin d'améliorer la lisibilité du texte.
Nous expérimenterons comment utiliser la reconnaissance faciale avec Tensorflow afin d'extraire certaines informations de la caméra, telles que la distance entre l'écran et le visage de l'utilisateur ou le nombre de personnes lisant la page. Ensuite, nous transmettrons ces données au CSS afin d'adapter la typographie et d'ajuster la mise en page.
Qu'est-ce que Tensorflow ?
Tensorflow est une plate-forme open source de Google pour l'apprentissage automatique. L'apprentissage automatique est un domaine de l'informatique qui étudie les algorithmes qui apprennent à reconnaître des relations complexes et des modèles récurrents à partir d'images, de pistes audio, de séries chronologiques, de texte naturel et de données en général. Ces algorithmes génèrent des modèles mathématiques (également appelés modèles formés), qui sont une sorte de schéma qui peut être utilisé pour prendre des décisions basées sur des données d'entrée. Si vous souhaitez aborder le sujet, Charlie Gerard a écrit sur ML pour les développeurs frontaux ici sur Smashing Mag.
Tensorflow fournit de nombreux outils pour les développeurs d'IA, les scientifiques des données, les mathématiciens, mais pas de panique si l'analyse des données n'est pas votre pain quotidien ! La bonne nouvelle est que vous n'avez pas besoin d'être un expert pour l'utiliser, tant que vous utilisez des modèles pré-construits, comme nous allons le faire.
Les modèles Tensorflow sont disponibles pour être utilisés sur le Web avec leur SDK JavaScript.
Installer
Pour commencer à utiliser les algorithmes de reconnaissance faciale, nous devons suivre quelques étapes :
- chargez le SDK Tensorflow.
- charger la bibliothèque Facemesh qui contient le modèle mathématique.
- accéder à la caméra de l'utilisateur et la diffuser dans un élément vidéo HTML. Facemesh analysera les images de la balise vidéo pour détecter la présence de visages.
Dans ce projet, nous allons utiliser Tensorflow via CDN, mais il est également disponible sur NPM si vous préférez la méthode bundler :
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl"></script>
Tensorflow ne fait pas l'affaire lui-même, nous devons donc ajouter Facemesh, une bibliothèque qui est construite au-dessus du framework ML et fournit un modèle déjà formé pour la reconnaissance faciale :
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/facemesh"></script>
L'étape suivante consiste à configurer la bibliothèque Facemesh afin de charger le modèle formé et de définir la fonction qui évaluera les données de visage à partir d'un flux vidéo :
// create and place the video const video = document.createElement('video'); document.body.appendChild(video); // setup facemesh const model = await facemesh.load({ backend: 'wasm', maxFaces: 1, }); async function detectFaces() { const faces = await model.estimateFaces(video); console.log(faces); // recursively detect faces requestAnimationFrame(detectFaces); }
Nous sommes maintenant prêts à demander à l'utilisateur l'autorisation d'accéder à son flux de caméra à l'aide d'une balise vidéo :
// enable autoplay video.setAttribute('autoplay', ''); video.setAttribute('muted', ''); video.setAttribute('playsinline', ''); // start face detection when ready video.addEventListener('canplaythrough', detectFaces); // stream the camera video.srcObject = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'user', }, }); // let's go! video.play();
La méthode navigator.mediaDevices.getUserMedia demandera l'autorisation et commencera à diffuser la caméra dans l'élément vidéo. Une fois acceptée, la caméra commencera à diffuser vers la balise vidéo, tandis que la console du navigateur enregistrera les informations de visage détectées par Facemesh.
Veuillez noter que les autorisations de la caméra nécessitent une connexion sécurisée https ou localhost : vous ne pouvez pas simplement ouvrir le fichier index.html. Si vous ne savez pas comment configurer un serveur local, consultez le serveur http pour Node ou suivez ce guide pour Python ou celui-ci pour PHP.
Cas 1. Ajuster la typographie à l'aide de l'appareil photo du smartphone
Nous naviguons sur le Web partout avec notre smartphone. Il fut un temps, il n'y a pas si longtemps, où nous avions l'habitude de prendre des trains ou des bus bondés et nous gardions le smartphone très près de nos yeux car il n'y avait pas de place. À de nombreux moments et endroits de notre journée, nous changeons souvent la position et l'inclinaison du smartphone, même si nous regardons le même site. La distance entre les yeux et le smartphone affecte nos capacités de lecture. En évaluant cette distance, nous pouvons ajuster la microtypographie afin d'optimiser les glyphes pour une lecture plus proche ou plus éloignée.
La détection de visage signifie, bien sûr, également la détection de la position des yeux. Nous pouvons utiliser les données fournies par Facemesh pour calculer la taille de notre visage par rapport à l'ensemble de l'image capturée par l'appareil photo. On peut supposer que plus notre visage est grand, plus nous sommes proches de l'écran. Nous pouvons mettre en place une échelle de 0 (un bras éloigné — le visage occupe approximativement la moitié de la caméra) à 1 (collé à l'écran) et détecter la valeur actuelle avec une division de segments :
async function detectFaces() { const faces = await model.estimateFaces(video); if (faces.length === 0) { // is somebody out there? return requestAnimationFrame(detectFaces); } const [face] = faces; // extract face surface corners let { bottomRight, topLeft} = face.boundingBox; // calculate face surface size let width = bottomRight[0] - topLeft[0]; let height = bottomRight[1] - topLeft[1]; let videoWidth = video.videoWidth; let videoHeight = video.videoHeight; let adjustWidth = videoWidth / 2; let adjustHeight = videoHeight / 2; // detect the ratio between face and full camera picture let widthRatio = Math.max(Math.min((width - adjustWidth) / (videoWidth - adjustWidth), 1), 0); let heightRatio = Math.max(Math.min((height - adjustHeight) / (videoHeight - adjustHeight), 1), 0); let ratio = Math.max(widthRatio, heightRatio); // recursively detect faces requestAnimationFrame(detectFaces); }

Maintenant que nous avons calculé le ratio
, il est temps de faire de la magie en passant la valeur à la feuille de style :

document.documentElement.style.setProperty('--user-distance', ratio);
Avec cette valeur et un peu de calcul, nous pourrions facilement appliquer de légères modifications au poids, à la taille et peut-être au style de la police, mais nous pouvons faire quelque chose d'encore mieux. En utilisant une police variable, une police qui a paramétré les formes et les espaces des glyphes, nous pouvons ajuster la perception de chaque glyphe en mettant à jour sa variation de taille optique.
Étant donné que chaque police variable utilise sa propre échelle pour les valeurs de taille optique, nous devons associer notre valeur de rapport à cette échelle. De plus, nous pouvons vouloir nous déplacer juste entre un sous-ensemble de taille optique disponible, afin de fournir juste de petites améliorations.
.main-text { --min-opsz: 10; --max-opsz: 15; --opsz: calc(var(--min-opsz) + (var(--user-distance) * (var(--max-opsz) - var(--min-opsz)))); ... font-family: 'Amstelvar', serif; font-variation-settings: 'opsz' var(--opsz); }
Vous pouvez le voir en direct ici. Veuillez noter que cet exemple n'est qu'une démonstration du fonctionnement de la technologie. Les changements typographiques doivent être presque imperceptibles aux yeux de l'utilisateur afin d'offrir une meilleure expérience de lecture. Ici, nous avons tiré parti des formes de glyphes, mais l'utilisation de couleurs pour augmenter ou diminuer les contrastes n'est qu'une autre bonne solution à essayer. Une autre expérience consistait à détecter l'angle du visage afin de calculer la perspective de lecture, en modifiant les ascendantes, les descendantes et la hauteur des lettres :
Voir le Pen [Facemesh and ascenders/descenders](https://codepen.io/smashingmag/pen/oNxrYop) par Edoardo Cavazza.
Cas #2 : Ajuster une mise en page lorsque le nombre de personnes à la recherche change
Dans ce deuxième cas, nous allons changer la disposition en fonction du nombre de personnes qui regardent l'écran. Nous pouvons imaginer un essai affiché sur le tableau blanc interactif dans le contexte d'une classe de lycée. Ce scénario est légèrement différent de celui détecté par la requête média de projection obsolète, car nous souhaitons ajuster la mise en page de la page si le nombre d'étudiants qui regardent est inférieur ou supérieur à 10. Lorsque seuls quelques étudiants sont dans la classe, ils peut s'approcher du tableau en toute sécurité, mais si toute la classe est présente, l'espace n'est probablement pas suffisant et nous devons modifier la disposition pour montrer moins (et plus) de choses.
Nous avons juste besoin de quelques modifications par rapport au script précédent afin de détecter correctement le nombre de visages qui regardent le tableau blanc. Tout d'abord, nous devons demander à Facemesh de détecter plusieurs visages :
const model = await facemesh.load({ backend: 'wasm', maxFaces: 30, });
Et ensuite, nous devons passer ce numéro à la feuille de style :
async function detectFaces() { const faces = await model.estimateFaces(video); document.documentElement.style.setProperty('--watching', faces.length); // recursively detect faces requestAnimationFrame(detectFace); }
Encore une fois, nous pourrions utiliser cette valeur pour simplement augmenter la taille de la police, mais notre objectif est de fournir une mise en page complètement différente. Les dispositions de grille CSS peuvent nous aider dans cette mission. Ce document projeté est un long formulaire avec un aparté qui contient des images liées :
<section> <article> <h1>...</h1> <h2>...</h2> <p>...</p> </article> <aside> <img src="..." alt="..." /> </aside> </section>
Et voici sa disposition par défaut :
section { display: grid; grid-template-columns: repeat(12, 1fr); grid-column-gap: 1em; width: 120ch; max-width: 100%; padding: 1em; } section article { grid-column: 1 / -5; } section aside { grid-column: 7 / -1; }

Lorsqu'un grand nombre de personnes regardent, il faut privilégier le contexte de lecture longue, en donnant plus d'espace à la colonne principale, en augmentant sa taille de police et en supprimant les éléments dérangeants. Pour ce faire, nous augmentons le nombre de colonnes étendues, en déplaçant le côté sous le texte principal.
:root { --watching: 10; } section { /** The maximum number of people watching for the default layout */ --switch: 10; /** The default number of columns for the text */ --text: 8; /** The default number of columns for the aside */ --aside: 4; grid-template-columns: repeat(calc(var(--text) + var(--aside)), 1fr); } section article { /** * Kinda magic calculation. * When the number of people watching is lower than --switch, it returns -2 * When the number of people watching is greater than --switch, it returns -1 * We are going to use this number for negative span calculation */ --layout: calc(min(2, (max(var(--switch), var(--watching)) - var(--switch) + 1)) - 3); /** * Calculate the position of the end column. * When --layout is -1, the calculation just returns -1 * When --layout is -2, the calculation is lower than -1 */ --layout-span: calc((var(--aside) * var(--layout)) + var(--aside) - 1); /** * Calculate the maximum index of the last column (the one "before" the aside) */ --max-span: calc(-1 * var(--aside) - 1); /** * get the max between --layout-span and the latest column index. * -1 means full width * --max-span means default layout */ --span: max(var(--max-span), var(--span)); grid-column-start: 1; grid-column-end: var(--span); }
- Vous pouvez le voir en direct ici →
À l'inverse, lorsqu'un petit groupe d'élèves expérimente le texte près du tableau, nous pourrions donner plus de détails, tels que des fichiers multimédias et des déclencheurs d'action interactifs.
Au-delà de la reconnaissance faciale
Les cas auxquels nous avons été confrontés () ne sont que deux exemples de la manière dont nous pouvons utiliser la technologie de reconnaissance faciale pour la mise en page ou les portées typographiques. Tensorflow fournit d'autres modèles et bibliothèques qui peuvent transformer le flux de la caméra en variables pour nos pages. De plus, il ne faut pas oublier que dans nos smartphones il y a beaucoup d'autres capteurs que nous pourrions exploiter en utilisant les Sensor APIs : GPS, accéléromètre, lumière ambiante, etc.
Étant donné que l'humeur influence la façon dont nous lisons, étudions et recherchons des informations, avec l'apprentissage automatique, nous pouvons également analyser les expressions de l'utilisateur pour passer d'une mise en page minimale à une mise en page détaillée en fonction de l'esprit de l'utilisateur.
Depuis de nombreuses années, nous sommes habitués à utiliser les requêtes CSS Media pour la conception Web réactive. Cependant, la taille de la fenêtre n'est qu'une des variables de l'expérience utilisateur. Récemment, un nouveau type de requête multimédia conçue pour respecter les préférences des utilisateurs a débarqué dans les navigateurs, comme prefers-color-scheme
et prefers-reduced-motion
. Cela donne aux concepteurs et aux développeurs un moyen de faire un pas en avant dans les pratiques de conception Web, permettant à la page Web de s'adapter à l'ensemble de l'environnement plutôt qu'à l'appareil de l'utilisateur. À l'ère du Big Data, nous avons la possibilité d'aller au-delà de la conception réactive et adaptative. Nos pages Web peuvent enfin « quitter l'écran » et faire partie de l'expérience globale de l'utilisateur. La conception d'interaction va impliquer toutes ces possibilités, donc continuer à expérimenter les combinaisons possibles entre la technologie et la conception Web sera crucial dans les années à venir.