processing / processing/p5.js

[p5.js 2.0+ Bug Report]: saveGif: delay option teleports frameCount instead of rendering warmup frames

Open
#8,888 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Area:Core Area:IO p5.js 2.0+
Dominant language
JavaScript
Stars
24k
Forks
3.8k
Avg merge
3d 16h
Merged PRs (30d)
25

Description

Most appropriate sub-area of p5.js?
  • Accessibility
  • Color
  • Core/Environment/Rendering
  • Data
  • DOM
  • Events
  • Image
  • IO
  • Math
  • Typography
  • Utilities
  • WebGL
  • WebGPU
  • p5.strands
  • Build process
  • Unit testing
  • Internationalization
  • Friendly errors
  • Other (specify if possible)
p5.js version

2.0.5

Web browser and version

149.0.7827.54 (Official Build) (64-bit) (cohort: Stable)

Operating system

Windows 11 Pro

Steps to reproduce this
Steps:
  1. Run the sketch below and wait until frameCount exceeds 300 (canvas turns dark, circle grows large)
  2. Note the frame number displayed on screen — this is where the animation currently is
  3. Click "Save GIF", then open the downloaded file
  4. Observe: the first frames of the GIF show a bright background, tiny circle, and a frame number near the delay value — not where the animation actually was
Snippet:

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/2.0.5/p5.min.js"></script>
</head>
<body>
<script>
  function setup() {
    createCanvas(480, 480);
    colorMode(HSB, 360, 100, 100);
    noStroke();
    let btn = createButton('Save GIF');
    btn.mousePressed(() => saveGif('demo', 3, { units: 'frames', delay: 30 }));
  }

  function draw() {
    let brightness = map(frameCount, 0, 600, 95, 5, true);
    let saturation = map(frameCount, 0, 600, 10, 90, true);
    background((frameCount * 0.3) % 360, saturation, brightness);
    let sz = map(frameCount, 0, 600, 20, 380, true);
    fill(((frameCount * 0.3) + 180) % 360, 80, 100);
    ellipse(width/2, height/2, sz);
    fill(0, 0, brightness > 50 ? 10 : 95);
    textSize(18);
    textAlign(CENTER, CENTER);
    text('frame ' + frameCount, width/2, height/2);
  }
</script>
</body>
</html>


Description:

saveGif has two related bugs in the same block of code that together make the delay option actively harmful rather than helpful.

Bug 1 — frameCount is overwritten with the delay value

Before recording begins, saveGif does:

let frameIterator = nFramesDelay;
this.frameCount = frameIterator; // ← line 39298 in 2.0.5
This teleports the sketch's frameCount to the delay value. If the animation has been running for 400 frames and you call saveGif('out', 3, { delay: 5, units: 'frames' }), the sketch's frameCount is set to 5 and the GIF renders from the animation's frame-5 state — not from frame 400 where you actually were.

Bug 2 — the delay renders zero warmup frames

The capture loop starts at frameIterator = nFramesDelay and runs until frameIterator < totalNumberOfFrames (nFrames + nFramesDelay). That's exactly nFrames iterations — every one of which is captured. There are no un-captured pre-capture renders. The delay parameter only changes where frameIterator starts counting; it never actually renders any warmup frames before capture begins.

This means the two bugs compound: even if you try to use a large delay to "skip past" the bad frames, Bug 1 ensures those frames are now from the wrong point in the animation entirely.

Why it matters: Coders often utilize frameCount as a variable guiding some aspect of their visuals. Because saveGif resets frameCount, that means the sketch gets set back to the first frame despite calling saveGif elsewhere. So the intended visuals are not saved. AND - the delay doesn't work as documentation intends.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in the saveGif implementation around line 39298 in p5.js 2.0.5 and trace frameIterator, frameCount, and the capture loop. Reproduce the issue with the supplied sketch, then verify that delay produces uncaptured warmup frames and that the GIF begins from the existing animation state without resetting frameCount.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
computer-graphics
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.