How to label your p5.js code
This tutorial will teach you how to use the describe(), describeElement(), gridOutput(), and textOutput() functions in your code so your canvases are readible by screen readers and other assistive technology.
What is screen reader labeling?
The canvas HTML element compresses the visuals created by your p5 code into a bitmap (a rastered graphic composed of pixels). This bitmap on its own doesn't provide any significant meaning or description about its contents to screen readers. That’s why the p5.js describe(), describeElement(), gridOutput(), and textOutput() functions exist— these allow you to manually add screen reader-accessible descriptions to your code so that screen reader technologies can describe the canvas’ content in a meaningful way.
Because a screen reader cannot naturally interpret the contents of a canvas bitmap, these functions add labels into your code that instruct the screen reader on how to describe certain elements within your canvas.
For more information about p5.js screen reader accessibility, please read Using p5 with a screen reader.
Screen reader labels for p5.js
p5.js offers four different functions for labeling your canvas:
- describe() provides an overall description of the canvas contents. This function's parameters include: text, which affords a string of text for the label; and display, an optional parameter to set the visibility of the label.
- describeElement() describes a specific element or a specific grouping of elements in a canvas. This function's parameters include: name, which affords a string naming the element described; text, which affords a string of text as the label description; and display, an optional parameter to set the visibility of the label.
- textOutput() generates a list providing a canvas description and its visual elements, including the canvas' size, canvas color, as well as each visual element’s color, position, and the amount of area the element covers within the canvas. This function's only parameter is display, which is an optional parameter to set the visibility of the label.
- gridOutput(), like textOutput(), generates a list of the canvas and its (visual) elements, only this function arranges its output in a HTML table that plots the spatial location of each shape within the canvas. It also provides a basic description of the canvas, including the canvas' size, canvas color, the number of visual elements, and the different visual element types inside the canvas. This function's only parameter is display, which is an optional parameter to set the visibility of the label.
Labeling best practices
What requires labeling?
As a good rule of thumb, any visual element that is important to the overall context or purpose of the canvas should be mentioned by the describe() and/or describeElement() functions.
Consider the overall purpose of the p5.js canvas and its contents in question, and label them in a way that makes sense for the message, functionality, and/or purpose of the canvas and its elements. In the code block below, a heart is made within the canvas by placing two circles on top of a triangle. Instead of individually labeling each shape used to make the heart, you should use one describeElement() function to describe the overall shape you made.
If, at any point, an element in your canvas undergoes a change or alteration in its visual appearance (and this change is important to the overall meaning and context of the canvas), it’s best to also update the describeElement() label when that change occurs.
The canvas HTML element will also rasterize any text within it. Use the describeElement() function to translate any significant text within your canvas.
In short, any significant visual, textual, or animated information within the canvas should be labeled with a screen reader label.
What DOESN'T need labeling
Individual interactive elements, such as HTML buttons, dropdowns, or inputs, do not need to be labeled. In the DOM, these elements are built outside of the p5.js canvas, and therefore can be interpreted by screen readers.
This means the gridOutput() and textOutput() functions will not provide any information about these interactive inputs in their reports of the canvas, should you use them.
How to use labels
For all canvases
No matter the canvas’ purpose or contents, you should always use a label to supply an overall summary of the canvas. Most often, you’ll use the describe() function for this summary.
The summary should provide a general understanding of the visuals inside the canvas.
For simple, non-animated canvases
For canvases without any changing, moving, or interactive elements, you may use either the describeElement(), gridOutput(), or textOutput() functions to label the canvas’ visual content. However, keep in mind that gridOutput() and textOutput() generate their information based on the rudimentary code of the visual element, such as its size, color, and shape. These functions won’t be able to interpret your intention in using such a shape within a larger visual built using multiple shapes.
Keep in mind the context and objective of the canvas’ contents when choosing which function(s) to use. Is it better to describe the flower as eight circles and a rectangle, or as a flower with red petals and a green stem? What kind of labeling will provide the best description of your canvas?
If you are creating larger, multi-shaped visuals, then it would be best to use describeElement() to define each visual grouping present within the canvas.
For interactive or animated canvases
If your canvas contains any animated elements or elements that change their visual form via user input (the click of a button, a dropdown selection, etc.), be sure that any descriptions of such elements update with their changes or animations. If you are using textOutput() or gridOutput() to describe the contents of your canvas, so long as these functions are placed within the draw() function, they will automatically update with the shape’s new information. If you are using describeElement(), use concatenation or another form of variable input to update the element’s description.
If this interaction or animation is crucial to the overall purpose and/or message of the canvas, be sure to mention in either the describe() label or the individual element’s label that this element is (or can be) animated.
Naturally-interactive HTML elements, such as buttons or inputs, do not need to have a label. Instead, follow the proper role and ID syntax for these elements when possible. For more information about how to properly label and ID HTML interactive elements, visit Mozilla’s HTML: A good basis for accessibility.
For complex canvases
The p5 functions listed above do not afford the more complicated features of ARIA labeling, such as aria-live or aria-atomic. For advanced canvases, using vanilla ARIA labeling and custom-built fallback content might better convey the canvas’ information to screen readers. Some cases where the canvas’ content cannot be accurately described (or represented) through p5.js’ supplied screen reader labels include:
- Canvases with content that changes extensively via external interactive elements.
- Canvases with content that requires the user’s attention if it is changed or altered by another element, especially if that element is not embedded in the canvas’ code.
- Canvases with complex element layouts that cannot be accurately represented using the describe(), describeElement(), textOutput(), or gridOutput() functions.
For more information about fallback content, visit W3C’s Wiki. For more information about complex ARIA labeling, visit Mozilla’s ARIA states and properties.
How NOT to use labels
As a substitution for poorly organized code
Screen reader labels should not be used as a way of commenting your code. These labels should only be used to summarize the resulting visual elements within a canvas.
As information overkill
If you overuse screen reader labels in your code, you may end up overly complicating the screen reader’s interpretation of the canvas rather than helping it.
Within reason, less is more with screen reader labeling. Be concise, accurate, and short with your label descriptions.
Do not use both the textOutput() and gridOutput() functions in the same canvas. You only need to use one or the other to describe the canvas elements. Using both will cause redundancy in the screen readers’ translation of your code. This also pertains to using textOutput() or gridOutput() with additional describeElement() labels. It’s best to choose one strategy of labeling for your entire canvas, and stick with it.
Testing labels during development
All these functions have a display parameter that either keeps its output only available to screen readers (FALLBACK) or presents the output as text adjacent to the canvas (LABEL). If you would like to see the output during development, add LABEL as the last parameter for the function. FALLBACK is the default parameter.
When testing your labels, consider the following questions: Do your canvas labels provide enough information for someone to understand its purpose? If this canvas exists on a website, or among any other content, would someone have a good understanding of how the canvas’ content interacts and/or pertains to the surrounding context?
In order to reduce redundancy, be sure to reset the display parameter to FALLBACK once you’ve tested the output. With LABEL active, screen readers will read the fallback text and the visible label text when focused on the canvas.
Notice any errors or typos? Please let us know. If you would like to contribute to this tutorial, feel free to issue a pull request!