Cache Static Drawing Layers with Processing createGraphics()
Use PGraphics offscreen buffers to draw static content once and reuse it each frame, cutting per-frame drawing cost in Processing sketches.
02 Aug 2026, 05:56 UTC

The problem: redrawing the same background every frame
In a typical Processing sketch, draw() runs repeatedly—often 60 times per second. If that function redraws a detailed grid, static UI panel, or repeated texture on every pass, Processing re-executes all those vector commands even when nothing has changed. The result is a lower frame rate and wasted CPU cycles for content that never moves.
The useful takeaway: draw static content once into an offscreen buffer, then draw that buffer as an image each frame. Processing provides createGraphics() for exactly this.
What createGraphics() and PGraphics do
createGraphics(width, height) returns a PGraphics object—an offscreen drawing buffer that lives outside the main display window. You issue drawing commands to it just as you would to the main canvas, but the results stay in memory until you explicitly draw them.
Because drawing to a buffer is not automatic, commands must be wrapped in beginDraw() and endDraw(). Omitting either call can leave the buffer blank or cause version-dependent errors. The buffer keeps its own transformation and coordinate state, so rotations or translations inside it do not leak into the main sketch.
An optional renderer argument—such as P2D or P3Dlets the buffer match the main sketch’s rendering backend. This matters if you use 3D shapes or custom shaders in both places.
Worked example: cache a static grid background
This sketch creates a 400-by-400 offscreen buffer in setup(), draws a static grid once, then renders it with image() inside draw(). A moving ellipse drawn on top proves the buffer is only a background layer.
PGraphics backgroundLayer;
void setup() {
size(400, 400, P2D);
// Create an offscreen buffer matching the main window size and renderer.
backgroundLayer = createGraphics(400, 400, P2D);
backgroundLayer.beginDraw();
backgroundLayer.background(245);
backgroundLayer.stroke(200);
backgroundLayer.strokeWeight(1);
// Static content: draw once, reuse every frame.
for (int x = 0; x <= 400; x += 40) {
backgroundLayer.line(x, 0, x, 400);
}
for (int y = 0; y <= 400; y += 40) {
backgroundLayer.line(0, y, 400, y);
}
backgroundLayer.endDraw();
}
void draw() {
// Draw the cached buffer instead of re-running the grid loops.
image(backgroundLayer, 0, 0);
// Dynamic content goes on top of the cached layer.
fill(255, 80, 80);
noStroke();
ellipse(mouseX, mouseY, 24, 24);
}
Paste this into the Processing IDE (Processing 3.x or 4.x), select Run, and watch the frame rate. The grid loops execute once during setup rather than every frame, while the moving ellipse still updates each pass.
Trade-offs and limitations
- Memory cost: Each buffer consumes memory roughly proportional to width times height. Multiple full-screen buffers can exhaust heap space, especially on systems with a small Processing memory allocation.
- Static content only: Caching helps when the layer changes rarely. If you modify the buffer every frame, you lose the benefit and add the overhead of managing an extra object.
- Explicit updates: To change cached content, call
beginDraw(), issue new commands, thenendDraw()again. Forgetting this cycle is a common cause of stale or blank output. - Display scaling: On high-density screens, an offscreen buffer can look blurry if its pixel density does not match the main window.
How to verify it works
- Run the example above and note the frame rate reported by Processing or printed with
println(frameRate). - Replace the
image(backgroundLayer, 0, 0)call with the original grid loops insidedraw()and run again. - Compare the two frame rates. If the grid is complex, the cached version often uses less CPU per frame.
- If you create several large buffers, monitor the sketch for slowdowns or
OutOfMemoryError, and reduce buffer dimensions or reuse a single buffer where possible.
Use createGraphics() when a layer is expensive to draw and changes infrequently. For everything that moves or updates each frame, keep it in draw() and draw it over the cached buffer.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.