Come utilizzare Face Motion per interagire con la tipografia
Pubblicato: 2022-03-10I web designer sono sempre alla ricerca di nuovi modi per migliorare la presentazione del contenuto di una pagina. A volte, questo può portare a soluzioni geniali o interagire con tecnologie spesso lontane dal campo del design. In questo articolo porteremo la tipografia in contatto con l'intelligenza artificiale, utilizzando l'apprendimento automatico per rilevare cose come la vicinanza del viso dell'utente al fine di migliorare la leggibilità del testo.
Sperimenteremo come utilizzare il riconoscimento facciale con Tensorflow per estrarre alcune informazioni dalla fotocamera, come la distanza tra lo schermo e il viso dell'utente o la quantità di persone che leggono la pagina. Quindi, passeremo questi dati ai CSS per adattare la tipografia e regolare il layout della pagina.
Cos'è il flusso tensoriale?
Tensorflow è una piattaforma open source di Google per Machine Learning. L'apprendimento automatico è un campo dell'informatica che studia algoritmi che imparano a riconoscere relazioni complesse e schemi ricorrenti da immagini, tracce audio, serie temporali, testo naturale e dati in generale. Questi algoritmi generano modelli matematici (chiamati anche modelli addestrati), che sono una sorta di schema che può essere utilizzato per prendere decisioni basate sui dati di input. Se desideri affrontare l'argomento, Charlie Gerard ha scritto di ML per sviluppatori frontend qui su Smashing Mag.
Tensorflow fornisce molti strumenti per sviluppatori di intelligenza artificiale, data scientist, matematici, ma niente panico se l'analisi dei dati non è il tuo pane quotidiano! La buona notizia è che non devi essere un esperto per usarlo, purché utilizzi modelli prefabbricati, proprio come faremo noi.
I modelli Tensorflow sono disponibili per essere utilizzati sul Web con il loro SDK JavaScript.
Impostare
Per iniziare a utilizzare gli algoritmi di riconoscimento facciale, dobbiamo seguire alcuni passaggi:
- caricare l'SDK Tensorflow.
- caricare la libreria Facemesh che contiene il modello matematico.
- accedere alla telecamera dell'utente e trasmetterla in streaming a un elemento video HTML. Facemesh analizzerà i fotogrammi del tag video per rilevare la presenza di volti.
In questo progetto utilizzeremo Tensorflow tramite CDN, ma è disponibile anche su NPM se si preferisce la modalità 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 non fa il trucco da solo, quindi dobbiamo aggiungere Facemesh, una libreria che è costruita sulla parte superiore del framework ML e fornisce un modello già addestrato per il riconoscimento facciale:
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/facemesh"></script>
Il passaggio successivo consiste nel configurare la libreria Facemesh per caricare il modello addestrato e definire la funzione che valuterà i dati del viso da un flusso video:
// 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); }
Ora siamo pronti a chiedere all'utente il permesso di accedere al suo stream della telecamera utilizzando un tag video:
// 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();
Il metodo navigator.mediaDevices.getUserMedia richiederà l'autorizzazione e inizierà a trasmettere in streaming la telecamera nell'elemento video. Una volta accettata, la telecamera inizierà a trasmettere in streaming al tag video, mentre la console del browser registrerà le informazioni sul viso rilevate da Facemesh.
Tieni presente che i permessi della fotocamera richiedono una connessione https sicura o localhost: non puoi semplicemente aprire il file index.html. Se non sei sicuro di come configurare un server http di checkout del server locale per Node o segui questa guida per Python o questa per PHP.
Caso 1. Regola la tipografia utilizzando la fotocamera dello smartphone
Navighiamo sul web ovunque con il nostro smartphone. C'è stato un tempo, non molto tempo fa, in cui prendevamo treni o autobus affollati e tenevamo lo smartphone molto vicino agli occhi perché non c'era spazio. In molti momenti e luoghi della nostra giornata, cambiamo spesso la posizione e l'inclinazione dello smartphone, anche se stiamo guardando lo stesso sito. La distanza tra gli occhi e lo smartphone influisce sulle nostre capacità di lettura. Valutando tale distanza possiamo regolare la microtipografia in modo da ottimizzare i glifi per una lettura più vicina o più lontana.
Il rilevamento del viso significa, ovviamente, anche il rilevamento della posizione degli occhi. Possiamo utilizzare i dati forniti da Facemesh per calcolare la dimensione del nostro viso in relazione all'intera immagine catturata dalla fotocamera. Possiamo presumere che più grande è la nostra faccia, più siamo vicini allo schermo. Possiamo impostare una scala da 0 (un braccio distante - il viso occupa circa la metà della fotocamera) a 1 (incollato allo schermo) e rilevare il valore corrente con una divisione di segmenti:
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); }

Ora che abbiamo calcolato il ratio
, è il momento di fare in modo che avvenga un po' di magia, passando il valore al foglio di stile:

document.documentElement.style.setProperty('--user-distance', ratio);
Con questo valore e un po' di calcolo potremmo facilmente applicare lievi modifiche al peso del carattere, alle dimensioni e forse anche allo stile, ma possiamo fare qualcosa di ancora meglio. Utilizzando un font variabile, un font che ha parametrizzato le forme e gli spazi dei glifi, possiamo regolare la percezione di ogni glifo aggiornandone la variazione di dimensione ottica.
Poiché ogni font variabile utilizza la propria scala per i valori delle dimensioni ottiche, dobbiamo mettere in relazione il nostro valore di rapporto con quella scala. Inoltre, potremmo voler spostarci solo tra un sottoinsieme delle dimensioni ottiche disponibili, al fine di fornire solo piccoli miglioramenti.
.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); }
Puoi vederlo dal vivo qui. Tieni presente che questo esempio è solo una dimostrazione di come funziona la tecnologia. I cambiamenti tipografici dovrebbero essere quasi impercettibili agli occhi dell'utente per fornire davvero una migliore esperienza di lettura. Qui abbiamo sfruttato le forme dei glifi, ma usare i colori per aumentare o diminuire i contrasti è solo un'altra buona soluzione da provare. Un altro esperimento è stato quello di rilevare l'angolo del viso per calcolare la prospettiva della lettura, modificando ascendenti, discendenti e l'altezza delle lettere:
See the Pen [Facemesh and ascenders/descenders](https://codepen.io/smashingmag/pen/oNxrYop) di Edoardo Cavazza.
Caso n. 2: regolazione di un layout quando cambia il numero di persone che guardano
In questo secondo caso, andremo a modificare il layout in base al numero di persone che guardano lo schermo. Possiamo immaginare un saggio visualizzato sulla lavagna interattiva nel contesto di un'aula di scuola superiore. Questo scenario è leggermente diverso da quello rilevato dalla query sui media di proiezione deprecata poiché vogliamo modificare il layout della pagina se il numero di studenti che guardano è inferiore o superiore a 10. Quando in classe sono presenti solo pochi studenti, possiamo tranquillamente avvicinarci alla lavagna, ma se è presente tutta l'aula, probabilmente lo spazio non è sufficiente e dobbiamo cambiare la disposizione per mostrare meno (e più grandi) cose.
Abbiamo solo bisogno di alcune modifiche allo script precedente per rilevare correttamente il numero di volti che guardano alla lavagna. Innanzitutto, dobbiamo indicare a Facemesh di rilevare più volti:
const model = await facemesh.load({ backend: 'wasm', maxFaces: 30, });
E poi, dobbiamo passare quel numero al foglio di stile:
async function detectFaces() { const faces = await model.estimateFaces(video); document.documentElement.style.setProperty('--watching', faces.length); // recursively detect faces requestAnimationFrame(detectFace); }
Ancora una volta, potremmo usare quel valore per aumentare semplicemente la dimensione del carattere, ma il nostro obiettivo è fornire un layout completamente diverso. I layout della griglia CSS possono aiutarci in questa missione. Questo documento proiettato è un modulo lungo con una parte che contiene immagini correlate:
<section> <article> <h1>...</h1> <h2>...</h2> <p>...</p> </article> <aside> <img src="..." alt="..." /> </aside> </section>
E questo è il suo layout predefinito:
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; }

Quando un gran numero di persone sta guardando, dobbiamo privilegiare il contesto di lettura in formato lungo, dando più spazio alla colonna principale, aumentandone la dimensione del carattere e rimuovendo gli elementi di disturbo. Per fare ciò, aumentiamo il numero di colonne con spanning, spostando il lato sotto il testo principale.
: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); }
- Potete vederlo dal vivo qui →
Viceversa, quando un piccolo gruppo di studenti sta sperimentando il testo vicino alla lavagna, potremmo fornire maggiori dettagli, come file multimediali e attivatori di azioni interattive.
Oltre il riconoscimento facciale
I casi che abbiamo affrontato () sono solo due esempi di come possiamo utilizzare la tecnologia di riconoscimento facciale per il layout o gli ambiti tipografici. Tensorflow fornisce altri modelli e librerie che possono trasformare il flusso della telecamera in variabili per le nostre pagine. Inoltre, non dobbiamo dimenticare che nei nostri smartphone ci sono molti altri sensori che potremmo sfruttare utilizzando le Sensor API: GPS, accelerometro, luce ambientale, ecc.
Poiché l'umore influenza il modo in cui leggiamo, studiamo e cerchiamo informazioni, con l'apprendimento automatico possiamo anche analizzare le espressioni degli utenti per passare da layout minimi a layout dettagliati in base allo spirito dell'utente.
Per molti anni siamo stati abituati a utilizzare le query CSS Media per il design web reattivo. Tuttavia, la dimensione del viewport è solo una delle variabili dell'esperienza utente. Di recente, un nuovo tipo di media query progettata per rispettare le preferenze dell'utente è approdato nei browser, come lo prefers-color-scheme
e prefers-reduced-motion
. Ciò offre a progettisti e sviluppatori un modo per fare un passo avanti nelle pratiche di progettazione web, consentendo alla pagina web di adattarsi all'intero ambiente anziché solo al dispositivo dell'utente. Nell'era dei big data, abbiamo l'opportunità di andare oltre il design reattivo e adattivo. Le nostre pagine web possono finalmente “uscire dallo schermo” ed entrare a far parte dell'esperienza globale dell'utente. Il design dell'interazione coinvolgerà tutte queste possibilità, quindi continuare a sperimentare le possibili combinazioni tra tecnologia e web design sarà fondamentale nei prossimi anni.