Skip to content

[p5.js 2.0+ Bug Report]: Framebuffer example breaks on 2+ #1571

Closed

Description

@ksen0

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+

Web browser and version

chrome

Operating system

macos

Steps to reproduce this

Steps:

  1. https://beta.p5js.org/examples/3d-framebuffer-blur/ observe somethign strange happening (intended: https://p5js.org/examples/3d-framebuffer-blur/). Instead of row of rotating spheres there's a smear of crescent shapes that fades into nothing
  2. if pasting code into p5.js editor, and select 2.3 in version, a whole other also strange behavior (also no syntax highlighting after 2.2?)

Activity

  1. vamshi567bh commented on Jun 20, 2026

    @vamshi567bh

    @davepagurek i would like to work on this please assign

  2. davepagurek commented on Jun 20, 2026

    @davepagurek
    Collaborator

    Thanks @vamshi567bh, although before assigning, can you do a bit of investigation? I suspect that at least part of the issue is that we do not have a built-in uModelViewMatrix any more; it has been split out into uModelMatrix and uViewMatrix. If you are able to make a modified version of the sketch just in the p5 editor that works with p5 2.3, we can determine if there is a bug in need of fixing in the p5 library or if we just need to update this example in the website repo to use the updated APIs.

  3. vamshi567bh commented on Jun 20, 2026

    @vamshi567bh

    @davepagurek I looked into this. I found the example code at src/content/examples/en/11_3D/06_Framebuffer_Blur/code.js.

    I checked the current 2.0 source and _setGlobalUniforms() in p5.Renderer3D.js still sets uModelViewMatrix unconditionally (computed as modelMatrix * viewMatrix). So the split you mentioned doesn't seem to be the direct cause in the current code, but you said it might have been.

    Here's a modified version with separate uModelMatrix + uViewMatrix to be safe:

    let vertexShader = `
    uniform mat4 uModelMatrix;
    uniform mat4 uViewMatrix;
    uniform mat4 uProjectionMatrix;
    
    attribute vec3 aPosition;
    attribute vec2 aTexCoord;
    varying vec2 vTexCoord;
    
    void main() {
      vTexCoord = aTexCoord;
      vec4 positionVec4 = vec4(aPosition, 1.0);
      gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * positionVec4;
    }`;

    I tested this modified vertex shader approach. Could you let me know if there are specific changes in 2.3 that might also affect how framebuffer depth textures work or how custom shader uniforms are bound?

  4. davepagurek commented on Jun 20, 2026

    @davepagurek
    Collaborator

    Off the top of my head I can't think of changes that would affect APIs, so the next thing I'd likely do is try to separate out the steps that are working from the ones that aren't. e.g. if you don't use the custom shader and just texture a place with the framebuffer's color channel, does it work? what about its depth channel? It also appears to not be animating, is it in fact still drawing every frame but it's just not making its way to the main canvas?

  5. vamshi567bh commented on Jun 20, 2026

    @vamshi567bh

    @davepagurek Matrix Multiplication Order in _setGlobalUniforms()

    In p5.Renderer3D.js:1437, the uModelViewMatrix is computed as:

    const modelViewMatrix = modelMatrix.copy().mult(viewMatrix);

    This computes modelMatrix * viewMatrix (model first, then view). However, the correct transformation pipeline should be:

    clipPosition = projection * view * model * localPosition
    

    So the combined model-view matrix should be viewMatrix * modelMatrix. The current code computes the reverse order, which is geometrically incorrect.

    When the vertex shader uses separate uModelMatrix and uViewMatrix uniforms (as in the updated example), the multiplication order in GLSL is:

    gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aPosition, 1.0);

    This is correct: model → view → projection.

    But the old vertex shader using uModelViewMatrix:

    gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);

    Expands to projection * model * view * position, which applies the view transform before the model transform — this is wrong and produces the crescent-shaped distortions visible in the broken example.

    The vertex shader change from uModelViewMatrix to uModelMatrix + uViewMatrix is not just a forward-compatibility change — it actually fixes the incorrect transformation. The uModelViewMatrix uniform is still set by _setGlobalUniforms() for backward compatibility, but on p5.js 2.0+ it produces wrong results due to the multiplication bug.

    The default camera (eyeZ=800, near=80, far=8000) compresses most scene depth into the [0.8, 1.0] range of the depth buffer. The hardcoded focal depth (0.9) is close to most objects' depth values, so the DOF blur effect is very subtle. The example may need either a narrower near/far ratio to spread depth values across more of the [0,1] range, or the focal point should be computed dynamically.

    I ran tests for all 5 debugging steps suggested in the linked comment. All compile without errors:

    1. Framebuffer color texture renders correctly via image()
    2. Color texture renders correctly via custom shader + plane()
    3. Depth texture has correct non-uniform values
    4. DOF shader produces correct output (blur amount matches depth)
    5. Animation loop works correctly (frame-to-frame updates)

    The uModelViewMatrix uniform is still set in _setGlobalUniforms() so shaders using it do compile, but produce geometrically incorrect results.

    The fix also needs to be applied to the example in the main p5.js repo (in src/content/examples/en/11_3D/06_Framebuffer_Blur/).

  6. vamshi567bh commented on Jun 20, 2026

    @vamshi567bh

    @davepagurek if ok i will go with the pr thanks!

  7. davepagurek commented on Jun 20, 2026

    @davepagurek
    Collaborator

    Good catch! I guess we didn't notice before because most shaders don't directly use that uniform. I'll assign this to you now, feel free to go ahead with a PR.

  8. vamshi567bh commented on Jun 20, 2026

    @vamshi567bh

    PR opened on p5.js-website: #1460

  9. davepagurek commented on Jun 20, 2026

    @davepagurek
    Collaborator

    I think we should also fix the matrix ordering in the p5 library too, if you're open to it

  10. davepagurek commented on Jun 20, 2026

    @davepagurek
    Collaborator

    @vamshi567bh I don't think we want to change the ordering of all matrix multiplies, that sounds like a super wide change that would have many unintended consequences. why not just flip the order of the model and view matrices in computing modelViewMatrix, as that's the only one not working correctly?

  11. vamshi567bh commented on Jun 21, 2026

    @vamshi567bh

    @davepagurek I looked into this more carefully. The current modelMatrix.copy().mult(viewMatrix) is already correct — it gives us the right result in the shader. Flipping the order would actually break things. The blur example works fine with the current code (I added tests in PR processing/p5.js#8948 to confirm). The issue was likely caused by something else that's already been fixed.

  12. vamshi567bh commented on Jun 21, 2026

    @vamshi567bh

    the current order modelMatrix.copy().mult(viewMatrix) is actually correct. #mult4x4 performs multiplication in column-major convention where A.copy().mult(B) gives uModelViewMatrix = view × model in column-major. When used in GLSL as uProjectionMatrix * uModelViewMatrix * position, this becomes projection × view × model × position — model is applied first, then view, then projection. That's the correct pipeline.

    Flipping to viewMatrix.copy().mult(modelMatrix) would give uModelViewMatrix = model × view in column-major, which in GLSL becomes projection × model × view × position — applying view before model, which is wrong.

    I verified this with new regression tests in PR processing/p5.js#8948 that confirm the full custom shader + depth texture pipeline works correctly with the current code. The Framebuffer Blur example works with the library as-is; the issue was likely an older build without the uSampler override fix (commits f75f227 / 355a04b) or a website example compatibility issue (addressed by PR processing/p5.js#1460 on p5.js-website).

  13. 17 remaining items

  14. BHARATH0153 commented on Aug 12, 2026

    @BHARATH0153

    @davepagurek thanks — that all makes sense

    1. Smaller spheres. sphere() currently defaults to r=50 while spacing is width/4, so they overlap badly when the sketch renders small. I'll drop the radius to something that reads cleanly at narrow widths.

    2. Space them in depth. The description says the closest and farthest spheres should appear blurred, but the row is currently all at z=0, so they share one depth value. I'll give each sphere a small z-offset (a shallow arc across the row, roughly ±40–60) so they sit at different depths, while keeping the spread small enough that they all stay visible when lined up horizontally on a small canvas.

    3. Shallower DOF via getBlurriness(). With the default camera the usable depth band is compressed (near 80 / far 8000 puts the row around depth 0.80–0.83), so the current abs(d - 0.9) * 40 produces almost no visible blur. I'll retune the focal depth to sit near the row's midpoint (≈0.82) and raise the blur multiplier so a small depth delta reads clearly. Starting values to verify visually in the web editor at desktop and mobile widths.

    4. Scope: push()/pop() around the blur pass, all changes confined to src/content/examples/en/11_3D/06_Framebuffer_Blur/code.js in p5.js-website. No library changes, and syntax highlighting is dropped as resolved.

  15. BHARATH0153 commented on Aug 12, 2026

    @BHARATH0153
  16. davepagurek commented on Aug 12, 2026

    @davepagurek
    Collaborator

    Hey everyone, I just wanted to leave a reminder that our AI policy disallows generation of comments without your review. Ultimately, even if intentions are good, these kinds of comments make it harder to triage and review issues, which makes it take longer for everyone for maintainers to respond. One of the things one can do to make it easier for maintainers to assign something to you is to show some evidence of having tested the claims in a comment, e.g. something like a screen recording or a link to a web editor sketch.

  17. ksen0 commented on Aug 13, 2026

    @ksen0
    MemberAuthor

    Update on this issue: this issu is open for volunteers again. Please do check the updated notes on v2 changes about custom shaders (here: https://github.com/processing/p5.js-compatibility#custom-shaders) - the PR closing this issue should target the p5.js-website repository, and update the example code to reflect v2 usage.

  18. bhabishnu commented on Oct 13, 2026

    @bhabishnu
    Contributor

    I reproduced this locally and tested a minimal example-only fix.

    There seem to be two separate things happening:

    1. In p5.js 2.x, the blur shader remains active into the next frame. Scoping the final blur pass with push()/pop() restores the intended sphere rendering.
    2. Once that is fixed, the original sphere size and blur curve make the depth-of-field effect difficult to read at the site's small embed size.

    The minimal candidate I found that works at both 300×150 and 800×600 is:

    • sphere(width / 16)
    • min(abs(d - 0.909) * 200., 4.)
    • push() / pop() around the final blur pass

    0.909 is the normalized depth of the rotation axis with the default camera, and the 4.0 cap keeps the blur from over-spreading at larger sizes.

    Here's the 300×150 comparison from the same test setup:

    Image

    Would this scope be appropriate for the Technical Initiative?

  19. ksen0 commented on Oct 13, 2026

    @ksen0
    MemberAuthor

    Thanks @bhabishnu ! I'll assign, that seems reasonable.
    But tagging @davepagurek in case blur shader behavior in 2x is unintended?

    EDIT: I've noticed you have some other work currently open so I won't assign additional tasks for now. Thanks for understanding

  20. davepagurek commented on Oct 13, 2026

    @davepagurek
    Collaborator

    Requiring a push/pop is intentional, adding the push/pop is good practice regardless.

    The other updates are I think required due to camera defaults changing + the display size of the example changing I believe, so doing whatever looks best to make it look good at its current display size also sounds good. We intentionally left it less robust originally because the original glsl has enough boilerplate to make it kind of imposing in length so the rest is quite minimal. Potentially if we convert this to p5.strands we can also make the logic a little more robust to different sizes without the code getting overwhelming?

  21. bhabishnu commented on Oct 14, 2026

    @bhabishnu
    Contributor

    @ksen0 Thanks for the heads up! A few of the issues I'm on are already at the PR/review stage, but I'm happy to hold off on picking up anything new for now.

    I should have a fair bit of time over the next couple of months though, so I'd still love to help out where useful. Would reviewing or testing other contributors' PRs be a good way to contribute in the meantime?

  22. bhabishnu commented on Oct 14, 2026

    @bhabishnu
    Contributor

    @davepagurek Thanks, that makes sense. I'll keep the fix focused on the current example and avoid making it any bigger than it needs to be.

  23. ksen0 commented on Oct 14, 2026

    @ksen0
    MemberAuthor

    @bhabishnu yes, adding reviews to open PRs and helping us juggle the current increase in incoming PRs would be helpful! In a good review, we expect that the reviewer asks constructively and helpfully for specific iteration, based on something they know well. If you're not sure about something in the PR it is best to make it clear that you're able to review X but not about Y. If you notice contributors would benefit from some resource (eg this one https://p5js.org/contribute/contributing_to_the_p5js_reference/ I sometimes link on PRs related to docs updates where the implementer has not been able to test locally) sharing that is also helpful.

  24. bhabishnu commented on Oct 14, 2026

    @bhabishnu
    Contributor

    @ksen0 Appreciate the guidance! I’ll keep an eye out for places where I can help with reviews and testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions