How To Use Face Motion To Interact With Typography

Web designers are always looking for new ways to improve the presentation of a page’s content. Sometimes, this can lead to ingenious solutions or to interact with technologies that are often kept away from the design field. In this article we will bring typography in contact with Artificial Intelligence, using machine learning to detect such things as the proximity of the user’s face in order to improve the legibility of the text.

We will experiment on how to use face recognition with Tensorflow in order to extract some information from the camera, such as the distance between the screen and user’s face or the amount of people reading the page. Then, we will pass those data to CSS in order to adapt typography and to adjust the page layout.

What Is Tensorflow?

Tensorflow is an open-source platform from Google for Machine Learning. Machine Learning is a field of Computer Science that studies algorithms that learn to recognize complex relations and recurring patterns out of images, audio tracks, time series, natural text, and data in general. These algorithms generate mathematical models (also called trained models), which are a sort of schema that can be used to make decisions based on input data. If you’d like to approach the topic, Charlie Gerard wrote about ML for frontend developers here on Smashing Mag.

Tensorflow provides a lot of tools for AI developers, data scientists, mathematicians, but don’t panic if data analysis is not your daily bread! The good news is that you have not to be an expert to use it, as long as you are using pre-built models, just as we are going to.

Tensorflow models are available to be used on the Web with their JavaScript SDK.

Setup

In order to start using face recognition algorithms, we need to follow a few steps:

  • load the Tensorflow SDK.
  • load the Facemesh library which contains the mathematical model.
  • access the user’s camera and stream it to an HTML video element. Facemesh will analyze frames from the video tag to detect the presence of faces.

In this projects we are going to use Tensorflow via CDN, but it is also available on NPM if you prefer the bundler-way:

Tensorflow does not do the trick itself, so we need to add Facemesh, a library that is built on the top of the ML framework and provides an already trained model for face recognition:

<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/facemesh"></script>

The next step is to setup the Facemesh library in order to load the trained model and to define the function that will evaluate face data from a video stream:

// 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);
}

Now we are ready to ask the user the permission to access to its camera stream using a video tag:

// 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();

The navigator.mediaDevices.getUserMedia method will prompt the permission and will start to stream the camera into the video element. Once accepted, the camera will start to stream to the video tag, while the browser console will log face information detected by Facemesh.

Please note that camera permissions require a secure https connection or localhost: you can not simply open the index.html file. If you are not sure how to set up a local server checkout http-server for Node or follow this guide for Python or this one for PHP.

Case 1. Adjust Typography Using The Smartphone Camera

We navigate the web everywhere with our smartphone. There was a time, not so long ago, where we used to take crowded trains or buses and we kept the smartphone very close to our eyes because there was no space. In many moments and places of our day, we often change the position and the inclination of the smartphone, even if we are watching the same site. The distance between eyes and smartphone affects our reading capabilities. Evaluating that distance we can adjust microtypography in order to optimize glyphs for a closer or more distant reading.

Face detection means, of course, eyes position detection too. We can use data provided by Facemesh to calculate the size of our face in relation with the whole picture captured by the camera. We can assume that the larger our face, the closer we are to the screen. We can set up a scale from 0 (one arm distant apart — the face approximately occupies one half of the camera) to 1 (glued to the screen) and detect the current value with a division of 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);
}

Now that we have calculated the ratio, it’s time to make some magic happens, passing the value to the stylesheet:

document.documentElement.style.setProperty('--user-distance', ratio);

With this value and a bit of calc we could easily apply slight changes to font weight, size, and maybe style too, but we can do something even better. Using a variable font, a font that has parameterized shapes and spaces of the glyphs, we can adjust the perception of every glyph by updating its optical size variation.

Since every variable font uses its own scale for optical size values, we need to relate our ratio value to that scale. Furthermore, we may want to move just between a subset of available optical size, in order to provide just little enhancements.

.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);
}

You can see it live here. Please note that this example is just a demonstration of how the technology works. Typographical changes should be almost imperceptible to the user’s eyes in order to really provide a better reader experience. Here we leveraged glyphs shapes, but using colors to increase or decrease contrasts is just another good solution to try. Another experiment was to detect the angle of the face in order to calculate the perspective of the reading, modifying ascenders, descenders ad the height of letters:

When a large number of people are watching, we need to privilege the long-form reading context, giving more space to the main column, increasing its font size, and removing disturbing elements. To do that, we increase the number of spanned columns, moving the aside below the main text.

: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);
}

Viceversa, when a small group of students is experiencing the text near the board, we could give more details, such as media files and interactive action triggers.

Beyond Face Recognition

The cases we faced (😉) are just two examples of how we can use face recognition technology for layout or typographical scopes. Tensorflow provides other models and libraries that can transform the camera stream into variables for our pages. Furthermore, we should not forget that in our smartphones there are a lot of other sensors that we could exploit using the Sensor APIs: GPS, accelerometer, ambient light, etc.

Since mood influences the way we read, study and search for information, with machine learning we can also analyze user’s expressions to switch from minimal to detailed layouts according to the user’s spirits.

For many years we have been used to using CSS Media queries for responsive web design. However, the size of the viewport is only one of the variables of the user experience. Recently, a new kind of media query designed to respect user preferences landed the browsers, such as the prefers-color-scheme and prefers-reduced-motion. This gives designers and developers a way to move a step forward in web design practices, allowing the web page to adapt to the whole environment instead of just the user’s device. In the age of big data, we have the opportunity to go beyond responsive and adaptive design. Our web pages can finally “leave the screen” and become part of the user’s global experience. Interaction design is going to involve all these possibilities, so keeping experimenting with the possible combinations between technology and web design will be crucial in the following years.

Modern CSS Techniques To Improve Legibility

Modern CSS Techniques To Improve Legibility

Modern CSS Techniques To Improve Legibility

Edoardo Cavazza

We can read in many ways, and there are many different types of readers, each with their own needs, skills, language, and, above all, habits. Reading a novel at home is different from reading it on the train, just as reading a newspaper is different from browsing its online version. Reading, like any other activity, requires practice for someone to become fast and efficient. Basically, we read better those things that we are used to reading the most.

Which aspects should we take into consideration when designing and developing for reading? How can we create accessible, comfortable, inclusive experiences for all readers, including the most challenged and those affected by dyslexia?

Articles Dedicated To Accessibility

At Smashing, we believe a good website is an accessible website, one which is available to everyone, no matter how they browse the web. We’ve highlighted just some of the many articles that we’re sure will help you create more accessible sites and web apps. Explore more articles →

Spaces, Words, Sentences, And Paragraphs

Units

On a web page, many units are available for us to adjust the font size of text. Understanding which unit to use is essential to setting the structure of a whole reading section. The reflowable nature of the web requires us to consider several aspects, such as the size of the viewport and the user’s reading preferences.

For this reason, the most suitable choices are generally em and rem, which are font-specific units. For example, setting the margins between paragraphs using ems helps to preserve the vertical rhythm as the text size changes. However, this can be a problem when a serif font is alternated with a sans-serif within a section. In fact, at the same font size, fonts can appear optically very different. Traditionally, the height of the lowercase “x” character (the x-height) is the reference for determining the apparent size of a character.

The comparison between the “d” and “x” glyphs of three different fonts at the same size reveal that their heights of the “x” (and therefore their optically size) are totally different
At the same font size, characters will optically appear very different. (Large preview)

Using the font-size-adjust rule, we can, therefore, optically render fonts of the same size, because the property will match the heights of the lowercase letters. Unfortunately, this property is currently available only in Firefox and in Chrome and Edge behind a flag, but it can be used as progressive enhancement using the @support check:

@supports (font-size-adjust: 1;) {
    article {
        font-size-adjust: 0.5;
    }
}

It also helps with the swap from the fallback font to the one loaded remotely (for example, using Google Fonts).

There are two articles. When switching the main font, the first article largely increase its length, since the font size is not adjusted to the x height, while the second one changes almost seamlessly
The first example shows how switching the font works normally. In the second one, we are using font-size-adjust to make the swap more comfortable. (Large preview)

Optimal Line Height

We think typography is black and white. Typography is really white [...] It is the space between the blacks that really makes it.

— Massimo Vignelli, Helvetica, 2007

Because typography is more a matter of “whites” than ”blacks”, when we apply this notion to the design of a website or web application, we must take into account special features such as line height, margins between paragraphs, and line breaks.

Setting the font size by relying on the x-height helps with optimizing the line height. The default line height in browsers is 1.2 (a unitless value is relative to the font size), which is the optimal value for Times New Roman but not for other fonts. We must also consider that line spacing does not grow linearly with the font size and that it depends on various factors like the type of the text. By testing some common fonts for long-form reading, combined with sizes from 8 to 14 points, we were able to deduce that, on paper, the ratio between the x-height and the optimal line spacing is 37.6.

A graph shows the relation between the ratio of x height and line height (y axis) and the ratio of x height and ascenders (x axis), with a downward trend from 38.1 to 35.8 fo the first ratio while increasing values of the x axis
Acceptable line-spacing ranges. (Large preview)

Compared to reading on paper, screen reading generally requires more spacing between lines. Therefore, we should adjust the ratio to 32 for digital environments. In CSS, this empirical value can be translated into the following rule:

p {
    line-height: calc(1ex / 0.32);
}

In the right reading contexts, this rule sets an optimal line height for both serif and sans-serif fonts, even when typographical tools are not available or when a user has set a font that overwrites the one chosen by the designer.

Define The Scale

Now that we have adjusted the font size and used the ex unit to calculate the line height, we need to define the typographical scale in order to correctly set the spacing between paragraphs and to provide a good rhythm to the reading. As said before, line spacing does not grow linearly but varies according to the type of text. For titles with a large font size, for example, we should consider a higher ratio for the line height.

article h1 {
    font-size: 2.5em;
    line-height: calc(1ex / 0.42);
    margin: calc(1ex / 0.42) 0;
}

article h2 {
    font-size: 2em;
    line-height: calc(1ex / 0.42);
    margin: calc(1ex / 0.42) 0;
}

article h3 {
    font-size: 1.75em;
    line-height: calc(1ex / 0.38);
    margin: calc(1ex / 0.38) 0;
}

article h4 {
    font-size: 1.5em;
    line-height: calc(1ex / 0.37);
    margin: calc(1ex / 0.37) 0;
}

article p {
    font-size: 1em;
    line-height: calc(1ex / 0.32);
    margin: calc(1ex / 0.32) 0;
}

Letter And Word Spacing

When working on legibility, we must also consider readers who are challenged, such as those with dyslexia and learning disabilities. Developmental dyslexia affects reading, and discussion and research regarding the causes are still ongoing. It is important to make use of scientific studies to understand the effects that visual and typographic variables have on reading.

For example, in a study that my company followed (“Testing Text Readability of Dyslexia-Friendly Fonts”), there was clear evidence that the glyph shapes of high-legibility fonts do not really assist reading, but wider spacing between characters (kerning) does. This finding was confirmed by another study on the effectiveness of increased spacing (“How the Visual Aspects Can Be Crucial in Reading Acquisition: The Intriguing Case of Crowding and Developmental Dyslexia”).

These studies suggest that we should exploit the dynamism and responsiveness of web pages by offering more effective tools, such as controls for handling spacing. A common technique when enlarging the size of characters is to adjust the spacing between letters and words through CSS properties such as letter-spacing and word-spacing.

See the Pen [Letter and word spacing](https://codepen.io/smashingmag/pen/KKVbOoE) by Edoardo Cavazza.

See the Pen Letter and word spacing by Edoardo Cavazza.

The problem with this is that letter-spacing acts unconditionally and breaks the kerning of the font, leading the page to render nonoptimal spaces.

Alternatively, we can use variable fonts to gain more control over font rendering. Font designers can parameterize spacing in a variable and non-linear way, and can determine how the weight and shape of a glyph can better adapt to the habits of the reader. In the following example, using the Amstelvar font, we are able to increase the optical size as well as spacing and contrast, as intended by the designer.

See the Pen [The optical size in variable fonts](https://codepen.io/smashingmag/pen/VweqoRM) by Edoardo Cavazza.

See the Pen The optical size in variable fonts by Edoardo Cavazza.

The Web.dev article “Introduction to Variable Fonts on the Web” has more detail on what variable fonts are and how to use them. And check out the Variable Fonts tool to see how they work.

Width And Alignment

To optimize reading flow, we also have to work on the width of the paragraph, which is the number of characters and spaces on a line. While reading, our eye focuses on about eight letters in a foveatio (i.e. the operation that is activated when we look at an object), and it is able to handle only a few consecutive repetitions. Therefore, line breaks are crucial: The moment of moving one’s focus from the end of a line to the beginning of the next is one of the most complex operations in reading and must be facilitated by keeping the right number of characters per type of text. For a basic paragraph, a common length is about 60 to 70 characters per line. This value can be easily set with the ch unit by assigning a width to the paragraph:

p {
    width: 60ch;
    max-width: 100%;
}

Justification also plays an important role in reading across lines. Hyphenation support for languages is not always optimal in the various browsers; therefore, it must be checked. In any case, avoid justified text in the absence of hyphenation because the horizontal spacing that would be created would be an obstacle to reading.

/* The browser correctly supports hyphenation */
p[lang=”en”] {
    text-align: justify;
    hyphens: auto;
}

/* The browser does NOT correctly support hyphenation */
p[lang=”it”] {
    text-align: left;
    hyphens: none;
}

Manual hyphenation can be used for languages that do not have native support. There are several algorithms (both server- and client-side) that can inject the &hyphen; entity within words, to instruct browsers where the token can be broken. This character would be invisible, unless it is located at the end of the line, whereupon it would render as a hyphen. To activate this behavior, we need to set the hyphens: manual CSS rule.

Foreground Contrast

The contrast of characters and words with the background is fundamental to legibility. The WCAG has defined guidelines and constraints for different standards (A, AA, AAA) governing the contrast between text and background. Contrast can be calculated with different tools, both for design and development environments. Keep in mind that automated validators are support tools and do not guarantee the same quality as a real test.

By using CSS variables and a calc statement, we can dynamically calculate the color that offers the best contrast with the background. In this way, we can offer the user different types of contrast (sepia, light gray, night mode, etc.), by converting the whole theme according to the background color.

article {
    --red: 230;
    --green: 230;
    --blue: 230;
    --aa-brightness: (
        (var(--red) * 299) +
        (var(--green) * 587) +
        (var(--blue) * 114)
    ) / 1000;
    --aa-color: calc((var(--aa-brightness) - 128) * -1000);
    background: rgb(var(--red), var(--green), var(--blue));
    color: rgb(var(--aa-color), var(--aa-color), var(--aa-color));
}

See the Pen [Automatic text contrast](https://codepen.io/smashingmag/pen/zYrygyr) by Edoardo Cavazza.

See the Pen Automatic text contrast by Edoardo Cavazza.

In addition, with the introduction and cross-browser support of the prefer-color-scheme media query, it becomes even easier to manage the switch from light to dark theme, according to user preference.

@media (prefers-color-scheme: dark) {
    article {
        --red: 30;
        --green: 30;
        --blue: 30;
    }
}

Going Forward

Designing and developing for optimal reading require a lot of knowledge and the work of many professionals. The more this knowledge is spread across the team, the better off users will be. Below are some points to lead us to good results.

For Designers

  • Consider semantic structure as being part of the project, rather than a technical detail;
  • Document layout and font metrics, especially the why’s and how’s of your choices. They will help developers to correctly implement the design;
  • Reduce typographic variables as much as possible (fewer families, styles, and variants).

For Developers

  • Learn the principles of typography in order to understand the design decisions made and how to implement them;
  • Use units relative to font size to implement responsive layouts (paddings, margins, gaps) that scale to user preferences;
  • Avoid unrestrained manipulation of font metrics. Legibility might suffer when font constraints are not respected.

For Teams

  • Read and understand the principles of the WCAG;
  • Consider inclusion and accessibility as part of the project (rather than separate issues).

Reading is a complex activity. Despite the many resources on web typography and the academic papers that identify areas for improvements, there is no magical recipe for achieving good legibility. The number of variables to consider might seem overwhelming, but many of them are manageable.

We can set the optimal line height of a paragraph using the ex unit, as well as set a paragraph’s width using the ch unit, in order to respect the user’s preferred browser settings for font size and family. We can use variable fonts to adjust the spacing between letters and words, and we can manipulate the stroke of glyphs to increase contrast, helping readers with visual impairments and dyslexia. We can even automatically adjust text contrast using CSS variables, giving the user their preferred theme.

All of these help us to build a dynamic web page whose legibility is optimized according to the user’s needs and preferences. Finally, given that every little implementation or technological detail can make a huge difference, it is still essential to test users’ reading performance using the final artifact.

Smashing Editorial (ra, yk, al, il)