toDataURL() not working with pc.DEVICETYPE_WEBGL2

Please take a look at this code in Codepen: https://codepen.io/cgopia95/pen/bNepXRO

If you click the button at the top/left, you can download the canvas image (just a green cube). This only works with “pc.DEVICETYPE_WEBGPU” in line #19, but if you commented it out and then uncomment line #18 to use ‘webgl2’, it does not generate the correct image but an empty one. I really need to use ‘webgl2’ instead of ‘webgpu’. Can you help?

Try this AI suggestion:

PlayCanvas WebGL2 Canvas Image Download Fix

Problem

When using PlayCanvas with WebGL2, calling canvas.toDataURL() returns an empty/transparent image, while the same code works correctly with WebGPU.

Root Cause

The preserveDrawingBuffer: true option is being passed to the Application constructor in graphicsDeviceOptions, but by that time the WebGL context has already been created by createGraphicsDevice() without that option.

In WebGL, when preserveDrawingBuffer is false (the default), the drawing buffer is cleared after each frame is presented to the screen. This means when you call canvas.toDataURL(), the buffer is already empty.

Solution

Move preserveDrawingBuffer: true into the createGraphicsDevice() options:

pc.createGraphicsDevice(canvas, { 
  deviceTypes: [pc.DEVICETYPE_WEBGL2],
  preserveDrawingBuffer: true  // Must be here, not in Application constructor
}).then((device) => {
    const app = new pc.Application(canvas, {
        mouse: new pc.Mouse(canvas),
        touch: new pc.TouchDevice(canvas),
        elementInput: new pc.ElementInput(canvas),
        graphicsDevice: device
        // graphicsDeviceOptions is not needed here
    });

    // ... rest of your code
});

Why WebGPU Works Without This

WebGPU handles the backbuffer differently. Its canvas configuration includes GPUTextureUsage.COPY_SRC which allows copying data out of the canvas, and WebGPU doesn’t have the same “drawing buffer is cleared after present” behavior that WebGL has by default.

Complete Working Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>PlayCanvas Green Cube Example</title>
    <script src="https://code.playcanvas.com/playcanvas-2.14.4.js"></script>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
<canvas id="application-canvas"></canvas>
<button id="download-btn" style="position:absolute;top:10px;left:10px;z-index:10;">Download Image</button>
<script>
    const canvas = document.getElementById('application-canvas');
    pc.createGraphicsDevice(canvas, { 
      deviceTypes: [pc.DEVICETYPE_WEBGL2],
      preserveDrawingBuffer: true
    }).then((device) => {
        const app = new pc.Application(canvas, {
            mouse: new pc.Mouse(canvas),
            touch: new pc.TouchDevice(canvas),
            elementInput: new pc.ElementInput(canvas),
            graphicsDevice: device
        });

        app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
        app.setCanvasResolution(pc.RESOLUTION_AUTO);
        app.start();

        // Create a camera
        const camera = new pc.Entity();
        camera.addComponent('camera', {
            clearColor: new pc.Color(0.2, 0.2, 0.2)
        });
        camera.setPosition(0, 0, 3);
        app.root.addChild(camera);

        // Create a directional light
        const light = new pc.Entity();
        light.addComponent('light');
        light.setEulerAngles(45, 30, 0);
        app.root.addChild(light);

        // Create a green material
        const material = new pc.StandardMaterial();
        material.diffuse = new pc.Color(0, 1, 0);
        material.update();

        // Create a cube
        const cube = new pc.Entity();
        cube.addComponent('model', {
            type: 'box'
        });
        cube.model.material = material;
        app.root.addChild(cube);

        cube.rotate(45, 45, 0);
    });

    // Download button handler
    document.getElementById('download-btn').addEventListener('click', function() {
        const dataURL = canvas.toDataURL('image/png');
        const link = document.createElement('a');
        link.href = dataURL;
        link.download = 'canvas-image.png';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });
</script>
</body>
</html>