Rendering

Getting graphics onto the screen is called rendering. There are many options for rendering and they all work a little differently.

WebGL

When we create a default canvas in p5.js with createCanvas(), we are using a renderer called P2D. A call to createCanvas(400, 400); is identical to createCanvas(400, 400, P2D);. As the name implies, P2D can only draw 2D graphics (lines, squares, images, etc). If we want to draw 3D graphics, we will need to use a different renderer.

p5.js can also run using the WebGL renderer. This is achieved by setting the third parameter of createCanvas() to WEBGL.

WebGL is a high performance renderer that allows rendering graphics in 2D and 3D. “Web” stands for… “web”, meaning it runs in the browser, and “GL” stands for “graphics library”. WebGL is part of a larger family of graphics libraries called OpenGL. OpenGL is a cross-language and cross-platform API (application programming interface) that has been around since 1991. This essentially all means that it runs virtually everywhere: on desktop computers, mobile phones, microcontrollers, probably even your smart refrigerator. All these devices have different capabilities (your gaming PC is a lot faster than your iPhone) and different variants of OpenGL exist for these platforms:

  • OpenGL Core (or simply OpenGL) is the original graphics library which runs on desktop computers.
  • OpenGL ES is a variant specifically for embedded systems (e.g. mobile phones). The library is a subset of OpenGL Core.
  • WebGL is a variant specifically for web browsers, and is based on OpenGL ES.

Note that this means that even though WebGL can run in a browser on a powerful PC, it will still be limited compared to running a standalone application using OpenGL Core on the same PC. On the flipside, this also means that your WebGL sketch will probably run the same on your desktop and on your phone.

While these variants have different feature sets, they use a familiar API (OpenGL functions have the same or similar names across the board) and more importantly for us, they all use the same shading language called GLSL. We will look at GLSL in depth when we start writing our own shaders.

Origin and Axes

Running a sketch in P2D or WEBGL may look similar at first glance but we will encounter some significant differences.

In the sketch above, you probably noticed that the circle is drawn under the mouse in P2D mode but not in WEBGL mode. This is because P2D and WEBGL use different origin points.

  • P2D uses the analogy of drawing on paper, where we usually start at the top-left of the page. This is just a convention adopted by the p5.js team. The origin (0, 0) could very well be in the bottom-left, bottom-right, center; it’s just a reference point. But (0, 0) in the top-left is very common, and is the convention adopted by many 2D drawing programs and frameworks (e.g. Photoshop, Processing, openFrameworks, etc.).
  • WEBGL uses the analogy of drawing in space. We can go forward or backwards in any of the three dimensions in space (up/down, left/right, front/back) so it makes more sense to set the origin at the center of the canvas for this mode. This is also a convention, but while most 3D drawing programs and frameworks agree that (0, 0, 0) should be in the center of the canvas, they unfortunately do not agree on which axes and directions X, Y, and Z represent. You can see this in the examples below, representing the axes in Blender, Unity, and p5.js.

In p5.js:

  • X moves towards the right.
  • Y moves towards the bottom.
  • Z moves towards the front.

X and Y move in the same direction in P2D and WEBGL. In order to use 2D positions in WEBGL (like the mouse position), all we need to do is move the origin. We can do this using the translate() function and moving everything by half the size of the window. In the following sketch, everything drawn after the call to translate() will take that offset into consideration.

Drawing Order

P2D has two dimensions, width and height. When shapes overlap, they are rendered in the order in which they are called.

WEBGL has a third dimension, depth, which it takes into account when rendering overlapping shapes. Objects that are further back get drawn behind objects in front of them, and the order in which they are called does not matter.

2D shapes like ellipse() and rect() only accept 2D parameters (x and y, width and height). In order to set their z parameter, we need to use the translate() function.