Text and type
In this tutorial, I’m going to show you how to use p5.js to display text in your sketch, and give your sketch different behaviors based on the shape of text in different sizes and fonts. We’ll also dive into string values, which are Javascript’s data type for storing and manipulating text. Finally, I’ll demonstrate how to load text from external files.
Text: the basics
You can display text to the screen in your sketch using the text()
function.
The text()
function takes at least three parameters: a string of characters
to display, and the X and Y coordinates where you want the text to be
displayed.
function setup() { createCanvas(400, 400); noLoop(); } function draw() { background(50); noStroke(); fill(255); text("Attention, please.", 50, 200); }
The color of the text is controlled with the fill()
function. You can
also use the stroke()
function to set the color of the text’s outline.
The default size of the font is 12 pixels. You can control the size of the font
with the textSize()
function. Here’s an example that shows all of this in
action. (Hold down the mouse button to turn the stroke on.)
function setup() { createCanvas(400, 400); } function draw() { background(50); strokeWeight(2); fill(128 + sin(frameCount*0.1) * 128); if (mouseIsPressed) { stroke(255); } else { noStroke(); } textSize(12 + (mouseX / width)*72); text("Attention, please.", 50, 200); }
You can change the text inside of the quotes ("
) to change the text that the
sketch will display. Try it out!
Text alignment
By default, the coordinate that you specify as the second and third parameters
of the text()
function specify where the
baseline of the text
should begin: that is, the lower-left corner of the first character in the
string. You can change this behavior with the textAlign()
function
The textAlign()
function, when called with a single parameter, controls the
horizontal alignment of the text, meaning where the text will br drawn
horizontally in relation to the coordinates you specify with the text()
function. The following sketch demonstrates the three possible values for
horizontal alignment. (The green circles are drawn at the same coordinates as
those in the text()
function calls, as a visual reminder.)
function setup() { createCanvas(400, 400); noLoop(); } function draw() { background(50); textSize(32); // left fill(0, 255, 0); ellipse(200, 100, 10, 10); fill(255); textAlign(LEFT); text("Tremendous!", 200, 100); // center fill(0, 255, 0); ellipse(200, 200, 10, 10); fill(255); textAlign(CENTER); text("Tremendous!", 200, 200); // center fill(0, 255, 0); ellipse(200, 300, 10, 10); fill(255); textAlign(RIGHT); text("Tremendous!", 200, 300); }
If you pass a second parameter to textAlign()
, it controls the vertical
alignment of the text, meaning where the text will be drawn vertically
in relation to the coordinates you specify. As mentioned above, the default is
to use the text’s baseline. Other values include TOP
(upper left-hand
corner of the text), BOTTOM
(lower left-hand corner, including any
descenders), and CENTER
(the
middle of the text.) The following sketch demonstrates:
function setup() { createCanvas(400, 400); noLoop(); } function draw() { background(50); textSize(32); // baseline (default) fill(0, 255, 0); ellipse(200, 100, 10, 10); fill(255); textAlign(CENTER, BASELINE); text("Stupendous!", 200, 100); // bottom fill(0, 255, 0); ellipse(200, 166, 10, 10); fill(255); textAlign(CENTER, BOTTOM); text("Stupendous!", 200, 166); // center fill(0, 255, 0); ellipse(200, 233, 10, 10); fill(255); textAlign(CENTER, CENTER); text("Stupendous!", 200, 233); // top fill(0, 255, 0); ellipse(200, 300, 10, 10); fill(255); textAlign(CENTER, TOP); text("Stupendous!", 200, 300); }
(The difference between BASELINE
and BOTTOM
is subtle, but important!)
Text… in a box
By default, the text you specify in a call to text()
is drawn in one
long line. If you want the text to wrap instead, you can include two extra
parameters to the text()
. This will cause p5.js to draw the text inside
the rectangle specified by the four coordinates. For example, in the
example below, the text is displayed within a 200x200 box drawn at the
current mouse position. (Hold down the mouse button to see a visual
representation of the box.)
function setup() { createCanvas(400, 400); } function draw() { background(50); fill(255); textSize(32); text("It was the best of times. Such good times.", mouseX, mouseY, 200, 200); if (mouseIsPressed) { noFill(); stroke(0, 255, 0); rect(mouseX, mouseY, 200, 200); } }
When you’re displaying text that is being automatically wrapped (or text that
has newline characters in it; see below), you can change the line spacing of
the text with the textLeading()
function, which sets the number of pixels
to display between each line:
function setup() { createCanvas(400, 400); } function draw() { background(50); fill(255); textSize(32); textLeading((mouseX / width) * 64); text("It was the best of times. Such good times.", 100, 100, 200, 200); }
With a value of zero, there’s no spacing between the lines (i.e., the lines are drawn on top of one another).
Custom fonts
The default font in p5.js is pretty boring. You can specify a different font
using the loadFont()
function, like so:
function setup() { createCanvas(400, 400); } function draw() { background(50); strokeWeight(2); fill(255); textFont("Comic Sans MS"); textSize(12 + (mouseX / width)*72); text("Attention, please.", 50, 200); }
When you call the textFont()
like this, the font with the specified name
must exist on the computer of the person using your sketch. This is probably
fine for a few common fonts,
but if you want to guarantee that the user will see exactly what you intended,
you should include the font you want to use with your sketch.
To do this, upload the .ttf
or .otf
file for the font, and load it in
preload()
with the loadFont()
function. This sketch uses the
Knewave font downloaded from
The League of Moveable Type.
let myFont; function preload() { myFont = loadFont("knewave.otf"); } function setup() { createCanvas(400, 400); } function draw() { background(50); strokeWeight(2); fill(255); textFont(myFont); textSize(12 + (mouseX / width)*72); text("Attention, please.", 50, 200); }
As you can see, the loadFont()
takes either a string as a parameter, or a
font value (as returned from the loadFont()
function).
Note that font licensing is complicated, and just because you have a font on your computer doesn’t mean that you have a legal right to use it for any purpose. (Awful and unreasonable, I know.) For freely usable fonts, you might try the League of Moveable Type (mentioned above), or Open Font Library.
Strings
This is all well and good, but what exactly are these quote marks? What do they mean? Where do they come from. Should we be afraid? I can answer some of these questions for you.
In Javascript, there’s a special data type called a string. A string is kind of like an array, except instead of storing a sequence of values, it stores a sequence (or “string”) of characters. Values of the string type have special methods associated with them that allow you to get smaller parts of the string (“substrings”), for making bigger strings out of multiple smaller strings, for breaking strings up into arrays of tokens (like words) and for locating the indices of a given substring. (Among many other things!)
To create a string value, enclose a sequence of characters inside of single- or double-quotes. You can then assign this value to a variable. (If you don’t put anything between the quotes, you’ve made an empty string, which can be useful if you’re planning on building the string up gradually as your program runs.)
let someText = "this is a test";
let someMoreText = 'This is a test'; // single quotes are OK too!
let oneChar = "a"; // string with a single character
let nothingYet = ""; // empty string
Just as with an array, you can get the length of a string (i.e., the number of
characters in it) with its .length
property:
let someText = "this is a test";
console.log(someText.length); // prints 14
Substrings
As mentioned above, string values have a variety of helpful methods. Let’s
start with the .substring()
method. If you call this method with two
parameters, it returns the portion of the string starting at the index given by
the first parameter and ending at (but not including) the index given by the
second parameter. (Like array indices, string indices are zero-based.) When
called with a single parameter, this method returns the portion of the string
starting with the given index up to the end of the string. Try it out by
running this code in an empty p5.js sketch:
let someText = "it was the best of times";
console.log(someText.substring(0, 1)); // prints "i"
console.log(someText.substring(7, 15)); // prints "the best"
console.log(someText.substring(19)); // prints "times"
Let’s use the .substring()
method to write a sketch that goes through
a string and displays it to the screen one character at a time:
let sourceText = "Life is short and art long"; let curIndex = 0; function setup() { createCanvas(400, 400); frameRate(10); } function draw() { background(50); fill(255); textSize(144); textAlign(CENTER, CENTER); text( sourceText.substring(curIndex, curIndex+1), width/2, height/2); curIndex++; if (curIndex > sourceText.length) { curIndex = 0; } }
A sketch to display a portion of the string, based on the mouse position:
let sourceText = "Life is short and art long"; function setup() { createCanvas(400, 400); frameRate(10); } function draw() { background(50); fill(255); textSize(32); textAlign(CENTER, CENTER); let middle = sourceText.length / 2; let left = middle - ((mouseX / width) * middle); let right = middle + ((mouseX / width) * middle); text( sourceText.substring(left, right+1), width/2, height/2); }
EXERCISE: Make a sketch that draws every character from a string at a random position on the screen. (Try using a
for
loop!)
Appending and combining strings
Say you have two strings from different sources and you want to combine them
into a single string. In Javascript, the easiest way to do this is with the +
operator. If the thing on the left and the thing on the right are both strings,
you end up with a combination of the two. Example:
let beginning = "it was the raddest of times, ";
let ending = "it was the baddest of times";
// prints "it was the raddest of times, it was the baddest of times"
console.log(beginning + ending);
To demonstrate this fun fact about strings, we’ll use Processing’s built-in
key
variable. We previously discussed the key
variable when I briefly
showed you how to use the keyTyped()
function to react to user input. The
key
variable contains the character the user most recently typed, and it’s
just a regular string value that you can use like any other string. In the
following example, the sketch begins with an empty string. Each time the user
types a key, the key they typed is appended to the end of the empty string.
In draw()
, the string is displayed. It’s a very simple text entry box!
let contents = ""; function setup() { createCanvas(400, 400); } function draw() { background(50); fill(255); textSize(24); text(contents, 50, 50, 300, 300); } function keyTyped() { contents += key; }
Parsing strings into arrays
The examples we’ve worked with so far have been acting on individual
characters of strings. In English, we might find ourselves wanting
occasionally to work with text using a little-known unit called the “word.”
Parsing arbitrary text into words isn’t an easy task, but there is an
easy approximation, which I intend to show you here. In order to accomplish
this, we’re going to use the string value’s .split()
method.
The .split()
method returns an array composed of parts of the original
string. It takes a single parameter, a separator, which determines what the
boundary for those parts will be. For example:
let commaSeparated = "parrish,allison,college professor,surly";
let parts = commaSeparated.split(",");
console.log(parts[2]); // displays "college professor"
If you use " "
as the separator (i.e., a single space character), the
resulting array will (roughly) have one element per word found in the
original string:
let someText = "it was the best of times";
let words = someText.split(" "); // evaluates to an array of strings
for (let i = 0; i < words.length; i++) {
console.log(words[i]);
}
The following sketch is an example of the .split()
method used in this
manner. It takes the string and splits it up into words, and displays those
random words spread around the sketch at random:
let sourceText = "Life is short and art long"; let words = sourceText.split(" "); function setup() { createCanvas(400, 400); noLoop(); } function draw() { background(50); textSize(72); textAlign(CENTER, CENTER); for (let i = 0; i < words.length; i++) { fill(random(255)); text(words[i], random(width), random(height)); } }
EXERCISE: Adapt the “one character per frame” example sketch above such that it displays one word per frame instead. Use the
.split()
method.
Escape notation
You’ll occasionally find yourself in a situation where you need to include an unusual character in a string. For example, if you’re using single quotes, you can’t include an actual single quote (or apostrophe) inside of the string; Javascript interprets it as an attempt to close the string, and it causes a syntax error:
let someText = 'the emperor's new clothes'; // syntax error!
In this case, you could just use double quotes to quote the string instead of single quotes. But then sometimes you have a string that contains both single and double quotes, like:
// still a syntax error!
let someText = '"Which clothes?" he asked. "The emperor's new, natch!"';
In order to tell Javascript that you mean a quote character not as a part of
your Javascript code, but as a literal part of the string, you can use an
escape character. An escape character is a character preceded by a backslash
(\
), which has a special meaning. For example, the escape character for
a single quote is \'
:
// at last: not a syntax error
let someText = '"Which clothes?" he asked. "The emperor\'s new, natch!"';
Likewise, you can include a double quote character inside of a double-quoted
string with the \"
escape character:
let someText = "She frowned. \"I don't know what you mean.\"";
Another common escape character is \n
, which tells Javascript to put a line
break in the string:
let hd = "more precious\nthan a wet rose\nsingle on a stem"; function setup() { createCanvas(400, 400); noLoop(); } function draw() { background(50); fill(255); textSize(32); text(hd, 50, 80); }
The Mozilla Developer Network has a full list of escape characters.
Loading external text
There isn’t really an easy way to include a lot of text in your p5.js sketch’s
code. Instead, you might choose to use text stored in an external file,
and then load it into your sketch at run time. Processing has a function called
loadStrings()
to facilitate this. The loadStrings()
function loads in a
text file and puts its content into an array of strings, with one array item
per line in the source file.
The loadStrings()
function only works with plain text files (i.e., you
can’t load in, e.g., Word documents or HTML files). (See
here for a more in-depth
discussion of plain text.)
For the next few examples, I’ll be using the text of
H.D.’s Sea
Rose. To use this text
file in your sketch, download the file and copy it to your sketch folder
(just like you did for image and sound files). As with loadImage()
and
loadFont()
, the loadStrings()
function must be called from within
preload()
.
Here’s a simple example that loads in the entire file and displays it to the screen, one line at a time:
let seaRoseLines; function preload() { seaRoseLines = loadStrings('sea_rose.txt'); } function setup() { createCanvas(400, 400); noLoop(); } function draw() { background(50); textSize(16); for (let i = 0; i < seaRoseLines.length; i++) { fill(128+(i*10)); text(seaRoseLines[i], 50, 50+i*20); } }
In this sketch, I use the built-in shuffle()
function to reorder the array
containing the lines at random:
let seaRoseLines; function preload() { seaRoseLines = loadStrings('sea_rose.txt'); } function setup() { createCanvas(400, 400); } function draw() { background(50); textSize(16); for (let i = 0; i < seaRoseLines.length; i++) { fill(128+(i*10)); text(seaRoseLines[i], 50, 50+i*20); } } function mousePressed() { shuffle(seaRoseLines, true); }
Text metrics
When working with most fonts, it’s difficult to tell how many pixels wide
a particular string will be when displayed to the screen. Processing
provides a textWidth()
function to help out with this. The textWidth()
function takes a string as a parameter, and returns the width in pixels for
that string, given the current font settings.
The following sketch uses textWidth()
to display the text of Sea Rose.
But instead of showing the actual word, it shows a rectangle whose width is
equal to the size of that word. (Hold the mouse button to display the word
as well.)
let seaRoseLines; function preload() { seaRoseLines = loadStrings('sea_rose.txt'); } function setup() { createCanvas(400, 400); } function draw() { background(50); textSize(16); noStroke(); textAlign(LEFT, TOP); for (let i = 0; i < seaRoseLines.length; i++) { let words = seaRoseLines[i].split(" "); let currentOffset = 0; for (let j = 0; j < words.length; j++) { let wordWidth = textWidth(words[j]); fill(128+(i*10)); rect(25+currentOffset, 25+i*20, wordWidth, 16); if (mouseIsPressed) { fill(0); text(words[j], 25+currentOffset, 25+i*20); } // four pixels between words currentOffset += wordWidth + 4; } } }
Further examples TK!
Further reading
- String reference at Mozilla Developer’s Network
- Dan Shiffman’s string notes