Using p5.js createGraphics for Efficient Offscreen Drawing
Learn how to draw static content once into an offscreen p5.Graphics buffer and reuse it each frame to cut draw calls and keep animations smooth.
22 Sept 2026, 15:21 UTC

Problem: Redrawing Complex Shapes Every Frame
When a p5.js sketch draws detailed patterns, textures, or large vector shapes inside the draw() loop, the CPU must repeat the same work each frame. On modest hardware this can drop the frame rate below 30 fps, making animations feel choppy.
Thesis: Offscreen Buffers Save Work
The createGraphics() function returns a p5.Graphics object that behaves like an invisible canvas. You can draw into it once, then reuse the result with image() each frame. If the buffer’s contents only change when the user interacts or after a timed event, the per‑frame workload drops dramatically.
Worked Example: Static Background with Moving Sprite
let bgBuffer;
let x = 0;
function setup() {
createCanvas(600, 400);
// Create an offscreen buffer the same size as the main canvas
bgBuffer = createGraphics(width, height);
bgBuffer.noStroke();
// Draw a complex static pattern once
for (let i = 0; i < 200; i++) {
bgBuffer.fill(random(255), random(255), random(255), 150);
bgBuffer.ellipse(random(width), random(height), random(50, 120));
}
}
function draw() {
background(30);
// Draw the pre‑rendered background from the buffer
image(bgBuffer, 0, 0);
// Animate a simple shape in front
fill(255, 100, 0);
noStroke();
ellipse(x, height / 2, 40, 40);
x = (x + 2) % width;
}
In this sketch the background consists of 200 semi‑transparent circles. Those circles are generated only once in setup(). The draw() loop then merely copies the buffer to the screen and moves a single orange circle. Because the heavy drawing happens outside the loop, the frame rate stays high even if you increase the number of background elements.
Trade‑off: Memory Usage and Update Frequency
Each pixel in the buffer occupies four bytes (RGBA). A buffer matching the canvas size of 600x400 px uses about 0.9 MB; doubling the dimensions quadruples the memory. If you modify the buffer every frame you lose the performance benefit and add unnecessary memory traffic. Use offscreen buffers only for content that changes infrequently—such as static backgrounds, pre‑computed UI elements, or layered parallax maps that shift only on user input.
Actionable Checklist
- Identify parts of your sketch that are expensive to draw and change rarely.
- Create a
p5.Graphicsbuffer withcreateGraphics(w, h)insetup(). - Draw the static content into the buffer once.
- In
draw(), render the buffer withimage(buf, 0, 0)before drawing dynamic elements. - Verify the improvement by watching the value returned by
frameRate()(you can log it to the console) and by opening the browser’s DevTools -> Memory panel to observe heap size while varying buffer dimensions. - If you notice the buffer being cleared or redrawn each frame, move that code out of
draw()or guard it with a change‑detecting condition.
By treating expensive, static drawing as a reusable texture, you keep the main render loop lightweight and your p5.js animations responsive across devices.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.