Skip to content

Instantly share code, notes, and snippets.

@nbogie
Created April 23, 2026 12:19
Show Gist options
  • Select an option

  • Save nbogie/8e3a415dc8c54cd0b668a01a2276e56c to your computer and use it in GitHub Desktop.

Select an option

Save nbogie/8e3a415dc8c54cd0b668a01a2276e56c to your computer and use it in GitHub Desktop.
p5 tutorial-authoring tips and gotchas: WIP
title Tutorial tips and gotchas
description A few collected tips for writing tutorials on the p5.js website
category 2.0
categoryIndex 0
featuredImage ../images/featured/intro-to-p5-strands.png
featuredImageAlt An abstract, cosmic scene with particles
relatedContent
references
en/p5/p5shader
authors
Neill Bogie

export const fourTicks="````";

import EditableSketch from "../../../components/EditableSketch/index.astro"; import { Code } from 'astro:components';

Help! The indentation of my code examples is being eaten

Experimentally it looks like the markdown converter is immediately eating two leading spaces from every line of a code span.

Example:

Input:

```jsx
<EditableSketch code={`
function setup(){
  //two spaces start this line
    //four spaces start this line
      //six spaces start this line
}
`} />
```

Output:

function setup(){
//two spaces start this line
  //four spaces start this line
    //six spaces start this line
}

Note, in the above output, two leading spaces have been eaten from every line that has them.

So far, the best solution to this is to add two intentionally sacrificial spaces at the start of each line in the code-block.

See also this PR where I did that for each EditableSketch (where the code is parsed with single-backticks (code)) processing/p5.js-website#1170

Seeing indentation-stripping

Here's a little change that can be made to <EditableSketch/> to show what's being sent through to that component. Essentially, we just echo the props.code into a `

 element:

Edit src/components/EditableSketch/index.astro:

<>
<pre style="border:1px solid red; padding: 0px;">{props.code}</pre>
<!-- <CodeEmbed {...removedStuff} /> -->
</>

<EditableSketch code={ `let myShader;

function indentationExample() { four(); four(); three(); two(); }

function setup() { four(200, 200, WEBGL); eight(1); }

function draw() { two(); four(); eight(); twelve(); } `} />

<EditableSketch code={` let myShader;

function thisLineIsIndentedTwo() { four(); six(); eight(); ten(); }

function thisLineIsNotIndented() { four(200, 200, WEBGL); eight(1); }

`} />

Demonstrating component use syntax in an mdx file

If you want to demonstrate how to use component elements (tags), without rendering the component, put the element(s) inside of a code-fence with triple backticks, but it can't be naked, it has to be given a label, like jsx, after the opening three backticks.

```jsx
<EditableSketch code={`
  your code
  goes here
`} />
```

Demonstrating triple backtick syntax in an mdx file

Surround the expression with ANOTHER code fence, with four backticks! (really). We can't show that here. <Code code={["", "```jsx", " //your code here", "```", "" ].join("\n")} lang={"mdx"}/>

Indentation stripping in markdown code spans

When you write

<EditableCode code={`
your code
goes here
`} />

Available code components

Diagram of available code components and their dependencies

<code></code> CodeContainer CodeContainerWithCopy CodeBlockWrapper

<iframe/> CodeFrame SketchEmbed CodeEmbed EditableSketch

CodeContainer

Defined in src/components/CodeContainer/index.astro Uses html <code></code>

CodeContainerWithCopy

Defined in src/CodeContainer/CodeContainerWithCopy.astro Uses CodeContainer (and CodeCopyButton)

CodeBlockWrapper

Defined in src/components/CodeBlockWrapper/index.astro Uses CodeContainerWithCopy

CodeEmbed

Defined in src/components/CodeEmbed/index.jsx

EditableSketch

Describe: Runs your code, allows editing, re-running Defined in src/components/EditableSketch/index.astro Uses CodeEmbed

SketchEmbed

Defined in src/components/SketchEmbed/index.astro

Uses CodeFrame

CodeFrame

Defined in src/components/CodeEmbed/frame.tsx Uses html <iframe/>

AnnotatedCode - code and comments in parallel

Defined in src/components/AnnotatedCode/index.astro

Here's an example of its use, from the intro to GLSL tutorial: src/content/tutorials/en/intro-to-glsl.mdx

### Vertex Shaders

Here's a simple vertex shader that applies the transformations and camera perspective supplied by p5.js:

<AnnotatedCode lang="glsl" code={({ begin, end }) =>
`${begin('precision')}
precision highp float;
${end('precision')}
${begin('attributes')}
attribute vec3 aPosition;
${end('attributes')}
${begin('uniforms')}
// The transform of the object being drawn
uniform mat4 uModelViewMatrix;
// Transforms 3D coordinates to
// 2D screen coordinates
uniform mat4 uProjectionMatrix;
${end('uniforms')}
${begin('main')}
void main() {
  // Apply the camera transform
  vec4 viewModelPosition =
    uModelViewMatrix * vec4(aPosition, 1.0);
  // Tell WebGL where the vertex goes
  gl_Position =
    uProjectionMatrix * viewModelPosition;  
}
${end('main')}`}>
  <Fragment slot="precision">
   The shader starts with a `precision` line. It can either be `lowp`, `mediump`, or `highp`. Using the highest quality is a good place to start to ensure your shaders look the same everywhere. On desktops and laptops, your GPU will likely use the highest quality regardless of what you write. On phones, using a lower quality might be faster, but it may make your shaders render differently.
  </Fragment>
  <Fragment slot="attributes">
    The *attributes* of the shader contain values that change per vertex, which p5.js uses to share information like the position of each vertex. The attribute in this shader is a `vec3`, meaning it contains a value for x, y, and z. Attributes are special variable types that are only used in the vertex shader and are typically provided by p5.js. When you use p5.js methods like `rect()` or `vertex()`, p5.js passes the vertex information to the shader for you automatically.
  </Fragment>
  <Fragment slot="uniforms">
    The *uniforms* of a shader are values that are constants for the whole shape being drawn. Each one in this shader happens to be a `mat4`, which is a type often used to represent transformations like translations, scales, and rotations. Multiplying a point by a `mat4` applies the transformation it represents to the point. The ones in this shader get provided automatically by p5.js, but we'll see later how you can provide your own custom uniforms. Note that the order of multiplication with matrices matters. In most cases you will write the matrix first and the value being multiplied by it second.
  </Fragment>
  <Fragment slot="main">
    All vertex shaders require a function, `main()`, within which we position our vertices by assigning a value to `gl_Position`. This value is in *clip space*, where x, y, and z values go from -1 to 1 as they go from one side to the other. Multiplying a 3D point by `uProjectionMatrix` does this conversion for us using p5.js's camera settings. Before we do that, this shader also multiplies by `uModelViewMatrix` to apply the accumulated transformations that were set before drawing the shape.
  </Fragment>
</AnnotatedCode>

Weird characters at start of lines in tutorials

Some existing tutorials have non-breaking space unicode characters at the start of the lines (U+00a0). These may show up as outlined rectangles in your editor. It's not clear why these have been used - possibly because they've been initially authored in google docs.
It is fine - good - to replace them with whitespace but check the indentation is correct in their final in-browser render as the markdown parser does eat the first two whitespace characters from each line of certain types of code block. See elsewhere in this document.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment