Skip to content

Conversation

ayushman1210
Copy link


🎬 Fix: Remove black frames at start of saveGif recordings

📋 Summary

Fixes the issue where saveGif() generates 1-3 black frames at the beginning of GIF recordings in p5.js 2.0+. This occurs because frame capture begins before the canvas is properly initialized and rendered.

🐛 Problem

When using saveGif() in p5.js 2.0+, the generated GIFs contain black frames at the start before the actual animation begins. This happens because:

  1. The canvas hasn't been rendered yet when frame capture starts
  2. The draw() loop hasn't executed for the first frame
  3. Frame capture begins immediately without ensuring the canvas state is ready

Example:

function draw() {
  background(200);
  fill(frameCount % 255);
  circle(50, 50, 25);
}

function keyPressed() {
  if (key === 's') {
    saveGif('mySketch', 3);
  }
}

Result: GIF starts with 2-3 black frames, then shows the animation.

✨ Solution

This PR implements the following fixes:

1. Initial Render for Clean Start

  • Forces an initial redraw() call with a 50ms delay when starting from frame 0
  • Ensures the canvas is fully initialized before frame capture begins
  • Only applies when resetting the animation (default behavior)

2. New startLoop Option

Adds a new configuration option to control frame count behavior:

// Default: Reset frameCount and record from beginning (existing behavior)
saveGif('animation', 3, { startLoop: true });

// New: Continue from current frameCount without reset
saveGif('animation', 3, { startLoop: false });

Benefits:

  • ✅ Maintains backward compatibility (default is true)
  • ✅ Enables recording animations from their current state
  • ✅ Preserves the intentional frameCount reset for complete animation capture
  • ✅ Provides foundation for future timing state management (e.g., millis() override)

3. Improved Frame Capture Logic

  • Separates delay frames from captured frames
  • Only captures frames after the delay period completes
  • Ensures frameIterator properly tracks both delay and capture phases

4. Proper State Restoration

  • Saves original frameCount before recording
  • Restores or advances frameCount appropriately after recording
  • Preserves original loop state and pixel density

🧪 Testing

Test Case 1: Default Mode (startLoop: true)

function keyPressed() {
  if (key === 's') {
    saveGif('test', 2, { startLoop: true });
  }
}

Expected: GIF starts from frame 0, NO black frames ✅

Test Case 2: Continue Mode (startLoop: false)

function keyPressed() {
  if (key === 's') {
    // After sketch has been running for a while
    saveGif('test', 2, { startLoop: false });
  }
}

Expected: GIF starts from current frame, NO black frames ✅

Test Case 3: With Delay

function keyPressed() {
  if (key === 's') {
    saveGif('test', 2, { delay: 1, startLoop: true });
  }
}

Expected: 1-second delay, then starts from frame 0, NO black frames ✅

📝 Changes Made

Modified Files

  • src/image/loading_displaying.js

Key Changes

  1. Added startLoop parameter to options (default: true)
  2. Changed frameIterator initialization from nFramesDelay to 0
  3. Added conditional frameCount reset based on startLoop value
  4. Added initial render with delay when startLoop: true
  5. Wrapped frame capture in if (frameIterator >= nFramesDelay) condition
  6. Added small delay after redraw() to ensure frame completion
  7. Updated state restoration to properly handle frameCount

🔄 Backward Compatibility

Fully backward compatible

  • Default behavior unchanged (still resets frameCount to 0)
  • Existing code works without modifications
  • New startLoop option is opt-in

🎯 Related Issues

Closes #8124

🙏 Acknowledgments

Thanks to the maintainer feedback regarding the intentional frameCount reset behavior and future plans for timing state management. This implementation provides a foundation for extending to other timing functions like millis() in the future.

Before (broken):

  • Frame 1: ⬛ Black
  • Frame 2: ⬛ Black
  • Frame 3: ✅ Content starts
  • Frame 4-60: ✅ Animation

After (fixed):

  • Frame 1: ✅ Content starts (Frame: 0)
  • Frame 2-60: ✅ Smooth animation
  • No black frames anywhere

💡 Future Enhancements

This change lays the groundwork for:

  • Overriding millis() during recording (mentioned by maintainer)
  • Additional timing state preservation options
  • More granular control over animation state during recording

Checklist:

  • Code changes implemented
  • Tested locally with multiple scenarios
  • No black frames in generated GIFs
  • Backward compatible
  • Documentation updated
  • Console logs removed (production ready)

nking07049925 and others added 30 commits July 7, 2025 22:03
Add GLSL-based noise(vec2) function to p5.strands (processing#7897)
…onymousFunctionCallbackFix

Fix inline anonymous functions causing a parsing error in p5.strands callbacks
…ms-instance

Fix p5.strands uniform calls, add instance mode construct
Enhance p5.strands noise() to support noise(x, y) and 4-octave fractal noise
Adding docs for `code` in the refrence.
Individual flags to disable part of FES by the user
- Add p5.esm.min.js output to rollup config with terser minification
- Include p5.esm.min.js in package.json files field for npm publishing
- Follows existing pattern of hidden sourcemaps for minified builds
davepagurek and others added 29 commits September 16, 2025 19:16
Fix: allow single-arg atan() outside strands; add unit test
[p5.strands] Significant refactor for p5.strands
…a-docs

Update createCamera docs since it no longer sets the active camera
Use FES internationalization within the minor version
Added tests for _sanitizeFontName function to validate font name sanitization rules.
…ssage-fix

typography error message fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.