contributor_docs/documentation_style_guide.md
Hello! Welcome to the guidelines for writing p5.js documentation. This document is a remix of the following resources:
Our community is large and diverse. Many people learn to code using p5.js, and a large subset of those people are students in K–12 classes. After reading this guide, you will know:
We use documentation.js to generate the p5.js API documentation from JSDoc comments in the p5.js source code.
Refer to the inline documentation guide for more information on how to structure the documentation comments and what tags to use.
It also discusses how to generate and preview the reference documentation to see your changes.
Please use American English (color, center, modularize, etc). See a list of American and British English spelling differences here.
Please use the Oxford comma ("red, white, and blue", instead of "red, white and blue").
Write simple, declarative sentences. Brevity is a plus: get to the point.
Write in the present tense: "Returns an object that...", rather than "Returned an object that..." or "Will return an object that...".
Start comments in upper case. Follow regular punctuation rules:
// Draws a fractal from a Julia set.
function drawFractal(c, radius, maxIter) {
// ...
}
Communicate the current way of doing things, both explicitly and implicitly. Use the idioms recommended in this guide. Reorder sections to emphasize favored approaches if needed. The documentation should be a model for best practices and approachable for beginners.
Documentation has to be brief but comprehensive. Explore and document edge cases. What happens for each combination of arguments? What bugs are most likely to appear in a beginner's code?
Spell names correctly: p5.js, CSS, HTML, JavaScript, WebGL. When in doubt, refer to an authoritative source like their official documentation.
Write documentation without bias towards any kind of person. While documenting particularly demanding/sensitive topics, take the time to educate yourself. Ensure that your writing doesn’t hurt or offend someone unintentionally.
While writing unbiased documentation:
Prefer wordings that avoid "you"s and "your"s. For example, instead of:
If you need to declare a variable, it is recommended that you use `let`.
use this style:
Always use `let` to declare variables.
Pronouns
| Recommended | Not Recommended |
|---|---|
| they | he or she |
| them | him or her |
| their | his or her |
| theirs | his or hers |
| themselves | himself or herself |
Accessibility terminology
The following terminology is adapted from the WordPress documentation guidelines for Writing inclusive documentation. For more background on people-first language, see the CDC's guide on Communicating With and About People with Disabilities.
| Recommended | Not Recommended |
|---|---|
| person with disability | the disabled, handicapped, differently abled, challenged, abnormal |
| person without disability | normal person, healthy person, able-bodied |
| has [disability] | victim of, suffering from, affected by, stricken with |
| unable to speak, uses synthetic speech | dumb, mute |
| deaf, low-hearing | hearing-impaired |
| blind, low-vision | vision-impaired, visually-challenged |
| cognitive or developmental disabilities | mentally-challenged, slow-learner |
| person with limited mobility, person with a physical disability | crippled, handicapped |
Choose meaningful code samples that cover the basics as well as gotchas. Only use advanced syntax if it is necessary to explain how a feature works. Don't draw five circles to explain something when one circle will convey the idea. The code samples themselves should follow the guidelines below.
// for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s on the first line of a block.// Bad.
let magicWord = 'Please'; // Remember this.
// Good.
// Remember this.
let magicWord = 'Please';
// Bad.
if (keyIsPressed === true) {
thing1();
// This is an important note.
thing2();
}
// Good.
if (keyIsPressed === true) {
thing1();
// This is an important note.
thing2();
}
// Bad.
//Remember this.
let magicWord = 'Please';
// Good.
// Remember this.
let magicWord = 'Please';
// for multiline comments.
// Bad.
/**
* I will use // for multiline comments.
* I will use // for multiline comments.
* I will use // for multiline comments.
* I will use // for multiline comments.
* I will use // for multiline comments.
*/
//Bad.
/*
I will use // for multiline comments.
I will use // for multiline comments.
I will use // for multiline comments.
I will use // for multiline comments.
I will use // for multiline comments.
*/
// Good.
// I will use // for multiline comments.
// I will use // for multiline comments.
// I will use // for multiline comments.
// I will use // for multiline comments.
// I will use // for multiline comments.
describe() to in p5.js example code, to add labels to your canvas so that it’s readable for screen readers.Why? It makes examples accessible to screen readers, and models how to write good canvas labels.
// Good.
function setup() {
createCanvas(100, 100);
describe('A red heart in the bottom right corner of a pink background.');
}
// Bad.
function setup() {
createCanvas(100, 100);
describe('heart shape');
}
// Good.
function draw() {
background(220);
fill(0, 255, 0);
ellipse(mouseX, 50, 40, 40);
// Label updates with shape's translation.
describe(`A green circle at x pos ${round(mouseX)} moving with the mouse pointer.`, LABEL);
}
Don’t use screen reader labels as a way of commenting your code. Labels should only summarize the resulting visual elements within a canvas.
Don’t overuse screen reader labels, as you may end up complicating the screen reader’s interpretation of the canvas rather than helping it.
Do make your label descriptions short and accurate. The recommended length for label descriptions is one to two sentences. Use full sentences for your labels, and write in the present tense when describing elements.
The above examples and suggestions are based on the Writing Accessible Canvas Descriptions tutorial. This tutorial gives more detailed guidance, and includes other ways to label your canvas, in addition to describe(): describeElement(), textOutput(), and gridOutput().
To understand the structure of p5.js’ web accessibility features for contributors, see the Web Accessibility Contributor Doc.
// Bad.
function setup() {
∙∙∙∙createCanvas(400, 400);
}
// Bad.
function setup() {
∙createCanvas(400, 400);
}
// Good.
function setup() {
∙∙createCanvas(400, 400);
}
// Bad.
function setup(){
createCanvas(400, 400);
}
// Good.
function setup() {
createCanvas(400, 400);
}
if and for. Place no space between the argument list and the function name.// Bad.
if(keyIsPressed === true) {
doStuff ();
}
// Good.
if (keyIsPressed === true) {
doStuff();
}
// Bad.
function setup () {
createCanvas (400, 400);
}
// Good.
function setup() {
createCanvas(400, 400);
}
// Bad.
let y=x+5;
// Good.
let y = x + 5;
Why? JavaScript's automatic semicolon insertion can lead to subtle bugs.
// Bad.
let x = 0
// Good.
let x = 0;
// Bad.
function f(x, y) {
// ...
}
// Good.
function vectorField(x, y) {
// ...
}
// Bad.
let OBJEcttsssss = {};
// Bad.
let this_is_my_object = {};
// Good.
let thisIsMyObject = {};
// Bad.
class player {
constructor(name) {
this.name = name;
}
}
// Good.
class Player {
constructor(name) {
this.name = name;
}
}
Why? JavaScript doesn't have private properties or methods.
// Bad.
class Spy {
constructor(secret) {
this._secret = secret;
}
}
// Good.
class Spy {
constructor(secret) {
this.secret = secret;
}
}
var to declare variables.Why? Variables declared with
varhave confusing scoping rules. These lead to subtle bugs.
// Bad, because it looks reasonable.
circle(x, y, 50);
var x = 200;
var y = 200;
// Good, because it throws a ReferenceError.
circle(x, y, 50);
let x = 200;
let y = 200;
let to declare variables. Avoid using const.Why? Variables declared with
letare easier to reason about than those declared withvar. Variables are often reassigned in sketches, so it's helpful to default tolet.
// Bad.
flower = '🌸';
var flower = '🌸';
const flower = '🌸';
// Good.
let flower = '🌸';
let declaration per variable or assignment.Why? It’s easier to read and to add new variable declarations.
// Bad.
let positions = getPositions(),
startSearch = true,
dragonball = 'z';
// Good.
let positions = getPositions();
let startSearch = true;
let dragonball = 'z';
Why?
letis block scoped and not function scoped.
// Bad - unnecessary search.
function getCharacter(name = 'default') {
let character = characters.find((c) => c.name === name);
if (name === 'default') {
return false;
}
if (character) {
return character;
}
return false;
}
// Good.
function getCharacter(name = 'default') {
if (name === 'default') {
return false;
}
let character = characters.find((c) => c.name === name);
if (character) {
return character;
}
return false;
}
++, --).Why? Unary increment and decrement statements are subject to automatic semicolon insertion. This can cause silent errors with incrementing or decrementing values. It's also more expressive to update variables with statements like num
+= 1instead ofnum++.
// Bad.
let num = 1;
num++;
--num;
// Good.
let num = 1;
num += 1;
num -= 1;
'' for strings.// Bad.
let name = "Hilma af Klint";
// Bad - template literals should contain interpolation or newlines.
let name = `Hilma af Klint`;
// Good.
let name = 'Hilma af Klint';
Why? Broken strings are hard to read and make code less searchable.
// Bad.
let essay = 'You see us as you want to see us: \
in the simplest terms, in the most convenient definitions.';
// Bad.
let essay = 'You see us as you want to see us: ' +
'in the simplest terms, in the most convenient definitions.';
// Good.
let essay = 'You see us as you want to see us: in the simplest terms, in the most convenient definitions.';
Why? Template strings have a concise syntax. They also provide proper newlines and string interpolation features.
let name = 'Dave';
// Bad.
text(name + ', this conversation can serve no purpose anymore. Goodbye.' + name, 0, 0);
// Good.
text(`${name}, this conversation can serve no purpose anymore. Goodbye.`, 0, 0);
Why? Backslashes harm readability.
// Bad.
let bad = '\'this\' \i\s \"quoted\"';
// Good.
let good = 'Air quotes make you look "cool".';
Use === and !== over == and !=.
Don't use shortcuts for booleans.
Why? It's easier to understand for beginners.
// Bad.
if (mouseIsPressed) {
// ...
}
// Good.
if (mouseIsPressed === true) {
// ...
}
// Bad.
if (name) {
// ...
}
// Good.
if (name !== '') {
// ...
}
// Bad.
if (collection.length) {
// ...
}
// Good.
if (collection.length > 0) {
// ...
}
Don't use switch statements unless it's necessary.
Use parentheses when mixing operators. The only exceptions are the arithmetic operators +, -, and **.
Why? It's easier to read and avoids subtle bugs.
// Bad.
let huh = a && b < 0 || c > 0 || d + 1 === 0;
// Good.
let huh = (a && b < 0) || c > 0 || (d + 1 === 0);
// Bad.
if (a || b && c) {
return d;
}
// Good.
if (a || (b && c)) {
return d;
}
// Bad.
let what = a + b / c * d;
// Good.
let what = a + (b / c) * d;
// Bad.
if (mouseIsPressed === true)
circle(mouseX, mouseY, 50);
// Better.
if (mouseIsPressed === true) circle(mouseX, mouseY, 50);
// Best.
if (mouseIsPressed === true) {
circle(mouseX, mouseY, 50);
}
else on the same line as the preceding if block’s closing brace.// Bad.
if (mouseIsPressed === true) {
thing1();
thing2();
}
else {
thing3();
}
// Good.
if (mouseIsPressed === true) {
thing1();
thing2();
} else {
thing3();
}
else block after an if block that always executes a return statement.// Bad.
function mouseIsOnLeft() {
if (mouseX < width * 0.5) {
return true;
} else {
return false;
}
}
// Good.
function mouseIsOnLeft() {
if (mouseX < width * 0.5) {
return true;
}
return false;
}
Why? It's easier to read.
// Bad.
if ((number === 123 || letters === 'abc') && mouseIsPressed === true && keyIsPressed === true) {
doStuff();
}
// Good.
if (
(number === 123 || letters === 'abc')
&& mouseIsPressed === true
&& keyIsPressed === true
) {
doStuff();
}
Don't use selection operators in place of conditionals.
// Bad.
refrigeratorIsRunning && goCatchIt();
// Good.
if (refrigeratorIsRunning === true) {
goCatchIt();
}
while or do-while loops unless it's necessary. Use for loops to iterate a fixed number of times.let numPetals = 7;
// Bad.
let i = 0;
while (i < numPetals) {
ellipse(0, 0, 20, 80);
rotate(PI / numPetals);
i += 1;
}
// Good.
for (let i = 0; i < numPetals; i += 1) {
ellipse(0, 0, 20, 80);
rotate(PI / numPetals);
}
for loops to iterate over arrays.Why? Pure functions are easier to reason about than side effects.
Use
forEach()/map()/every()/filter()/find()/findIndex()/reduce()/some()/...to iterate over arrays. UseObject.keys()/Object.values()/Object.entries()to produce arrays for iterating over objects.
let diameters = [50, 40, 30, 20, 10];
// Bad.
for (let i = 0; i < diameters.length; i += 1) {
circle(0, 0, diameters[i]);
}
// Bad.
for (let d of diameters) {
circle(0, 0, d);
}
// Good.
diameters.forEach((d) => circle(0, 0, d));
// Bad.
let ball = new Object();
// Good.
let ball = {};
Why? It's easier to read and improves syntax highlighting. JavaScript engines also have an easier time optimizing for performance.
// Bad.
let secretObject = {
'x': 100,
'y': 200,
'top-secret': 'classified',
};
// Good.
let secretObject = {
x: 3,
y: 4,
'top-secret': 'classified',
};
let turtle = {
name: 'Leonardo',
color: 'dodgerblue',
weapon: '🗡️',
food: '🍕',
};
// Bad.
let turtleName = turtle['name'];
// Good.
let turtleName = turtle.name;
[] to access properties with a variable.let turtle = {
name: 'Leonardo',
color: 'dodgerblue',
weapon: '🗡️',
food: '🍕',
};
function getProp(prop) {
return turtle[prop];
}
let turtleName = getProp('name');
// Bad.
let mathematician = {
firstName: 'Ada'
, lastName: 'Lovelace'
};
// Good.
let mathematician = {
firstName: 'Ada',
lastName: 'Lovelace',
};
// Bad.
let artist = {
firstName: 'Lauren',
lastName: 'McCarthy'
};
// Good.
let artist = {
firstName: 'Lauren',
lastName: 'McCarthy',
};
// Bad.
let images = new Array();
// Good.
let images = [];
let lyrics = [];
// Bad.
lyrics[lyrics.length] = 'Little rough around the edges, but I keep it smooth';
// Good.
lyrics.push('Little rough around the edges, but I keep it smooth');
slice() method to copy arrays.// Bad.
let numbersCopy = [];
for (let i = 0; i < numbers.length; i += 1) {
numbersCopy[i] = numbers[i];
}
// Good.
let numbersCopy = numbers.slice();
// Bad.
let matrix = [[1, 0, 0],
[0, 1, 0],
[0, 0, 1]];
// Good.
let matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
];
// Also good.
let matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
Why? Function declarations have some gotchas, but they are easier to understand for beginners.
// Bad.
let foo = function () {
// ...
};
// Bad.
let foo = () => {
// ...
};
// Good.
function foo() {
// ...
}
// Bad.
function createBall(diameter) {
diameter = diameter || 50;
// ...
}
// Good.
function createBall(diameter = 50) {
// ...
}
// Bad.
function drawSpiral(angle = 90, length) {
// ...
}
// Good.
function drawSpiral(length, angle = 90) {
// ...
}
Why? The syntax is more concise. It also creates a version of the function that executes in the context of
this, which is often helpful.
Why not? If the anonymous function is complex, rewrite it as a declared function.
// Bad.
function setup() {
loadImage('assets/moonwalk.jpg', function (img) {
image(img, 0, 0);
});
}
// Good.
function setup() {
loadImage('assets/moonwalk.jpg', (img) => {
image(img, 0, 0);
});
}
// Bad.
function preload() {
loadImage('assets/moonwalk.jpg', (img) => {
// Complex preprocessing...
});
}
// Good.
function preload() {
loadImage('assets/moonwalk.jpg', processImage);
}
function processImage(img) {
// Complex preprocessing...
}
return statement.Why? It's easier to read.
// Bad.
[1, 2, 3].map((number) => {
let squared = number ** 2;
`${number} squared is ${squared}.`;
});
// Bad.
[1, 2, 3].map((number) => {
let squared = number ** 2;
return `${number} squared is ${squared}.`;
});
// Good.
[1, 2, 3].map((number) => `${number} squared is ${number ** 2}.`);
Why? Doing so reduces bugs when changing parameters.
// Bad.
[1, 2, 3].map(number => number * number);
// Good.
[1, 2, 3].map((number) => number * number);
Why? To accommodate users who may not be familiar with the concept of function chaining.
// Bad.
fill(0)
.strokeWeight(6)
.textSize(20);
// Bad.
fill(0).strokeWeight(6).textSize(20);
// Good.
fill(0);
strokeWeight(6);
textSize(20);
class. Avoid manipulating prototype directly. The only exception is explaining how to create libraries.Why?
classsyntax is more concise and easier to reason about.
// Bad.
function Mover(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
Mover.prototype.update = function () {
this.x += 1;
this.y += 1;
};
Mover.prototype.render = function () {
circle(this.x, this.y, 2 * this.radius);
};
// Good.
class Mover {
constructor(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
update() {
this.x += 1;
this.y += 1;
}
render() {
circle(this.x, this.y, 2 * this.radius);
}
}
extends for inheritance.class RandomMover extends Mover {
update() {
this.x += random(-1, 1);
this.y += random(-1, 1);
}
}
toString() methods don't cause side effects.// Bad.
class Mover {
// ...
toString() {
this.x += 1;
return `Mover at (${this.x}, ${this.y})`;
}
}
// Good.
class Mover {
// ...
toString() {
return `Mover at (${this.x}, ${this.y})`;
}
}
Why? Classes have a default constructor if one isn't specified.
// Bad.
class Dot {
constructor() {}
render() {
circle(mouseX, mouseY, 50);
}
}
// Good.
class Dot {
render() {
circle(mouseX, mouseY, 50);
}
}
// Bad.
class DragonBall extends Ball {
constructor(x, y, d) {
super(x, y, d);
}
}
// Good.
class DragonBall extends Ball {
constructor(x, y, d, numStars) {
super(x, y, d);
this.numStars = numStars;
}
}
Why? Duplicate class member declarations prefer the last one. Having duplicates often means there's a bug.
// Bad.
class Mover {
// ...
update() {
this.x += this.xspeed;
this.y += this.yspeed;
}
update() {
this.x = 0;
this.y = 0;
}
}
// Good.
class Mover {
// ...
update() {
this.x += this.xspeed;
this.y += this.yspeed;
}
reset() {
this.x = 0;
this.y = 0;
}
}
Whether assets (such as images, fonts, sound files, etc) are being used in the descriptions or code examples of the reference documentation, or in tutorials, the same rules apply:
assets.public/assets folder of the p5.js-website repository.Why? It models good project organization. It's also required for assets to load on the p5.js website.
let img;
// Bad.
async function setup() {
img = await loadImage('moonwalk.jpg');
//...
}
// Good.
async function setup() {
img = await loadImage('assets/moonwalk.jpg');
//...
}
There is more on working with assets in the guide: Contributing to the p5.js reference.