Glossary
Push and Pop

push() and pop()

In the 2D camera tutorial (which you might have just come here from), push() and pop() appear alongside every translate() call. Back there we described them as "bookmarks" — but what exactly are they bookmarking, and why does it matter?

The transform state

Every p5 sketch maintains an internal transform state: the current position, rotation, and scale of the coordinate system. When you call translate(100, 50), you're not moving your shapes — you're moving the entire coordinate system. Every drawing call that follows will be offset by (100, 50). You can think of it as a way of drawing on a page not by moving your pen or pencil, but by moving the page underneath as you draw.

The catch is that transformations are cumulative. Calling translate(100, 0) twice shifts the origin by 200 total, not 100. Here's what that looks like in practice: two rectangles, each trying to position themselves independently — but without any bookmarks to separate them.

Both rects are drawn at (0, 0) — their actual screen position comes entirely from the accumulated translate() calls. The second translate() didn't start from the canvas origin; it added to wherever the first one left off. This gets out of hand fast when you have many objects.

Saving and restoring

push() and pop() let you isolate transform changes to a local block:

  • push() saves the current transform state onto a stack
  • pop() restores the most recently saved state, discarding any transforms applied since

Here's the same two rectangles, now with each transform wrapped:

Each push()/pop() pair creates an isolated scope for its own translate(). Now you can move the first rect without the second one going along for the ride.

Nesting

push() and pop() can be nested. The name "stack" is literal — each push() adds a layer, and each pop() removes the most recent one. The animated translation example from the 2D camera tutorial works exactly this way:

push();
  translate(width / 2, height / 2);    // move to center
  push();
    translate(sin(frameCount * 0.025) * 120,
              cos(frameCount * 0.025) * 120);
    drawGrid();
    drawOrigin();
  pop();   // undo the orbital offset
pop();     // undo the center translation

The inner pop() only removes the animated offset — the outer push is still active until the outer pop().

Why the camera uses it

The Camera class's begin() and end() methods are a direct application of this pattern:

begin() {
  push();
  translate(this.translation.x, this.translation.y);
}
end() {
  pop();
}

Everything drawn between cam.begin() and cam.end() lives in the camera's translated coordinate space. Anything drawn outside that pair — a score display, a HUD, an overlay — is completely unaffected. Without push() and pop(), you'd have to manually un-translate the origin after every camera draw.

The same applies to rotate() and scale()

push() and pop() save all transform state — not just position. rotate() and scale() calls are isolated the same way. This makes independent per-object transforms straightforward:

Each rectangle has its own rotate() and both translate() calls, and none of them bleed into the next iteration — because every pop() rewinds the stack to where it was before that loop body's push().