Set a Custom Canvas Target in GPU.js

This article explains how to direct graphical output from GPU.js to a custom HTML5 <canvas> element. Because WebGL contexts cannot be rebound to new elements once created, routing output requires either attaching your canvas during the GPU instance setup for maximum performance or blitting the rendered frames from a compiled graphical kernel onto a secondary canvas dynamically.

Direct Method: Passing a Custom Canvas to the GPU Instance

The most performant way to render to a custom canvas is to attach the DOM element directly when instantiating the GPU object. A graphical kernel created under this instance will render straight to your custom canvas without requiring data copies.

<canvas id="render-surface" width="512" height="512"></canvas>
const targetCanvas = document.getElementById('render-surface');

// Bind the custom canvas during GPU initialization
const gpu = new GPU({
  canvas: targetCanvas,
  mode: 'webgl2' // Falls back to 'webgl' if unsupported
});

// Create a graphical kernel
const renderKernel = gpu.createKernel(function() {
  const x = this.thread.x / this.output.x;
  const y = this.thread.y / this.output.y;
  this.color(x, y, 0.5, 1.0);
})
.setOutput([targetCanvas.width, targetCanvas.height])
.setGraphical(true);

// Execute the kernel to render directly onto targetCanvas
renderKernel();

When .setGraphical(true) is enabled, the kernel draws its pixel values using this.color() into the WebGL context bound to targetCanvas.

Dynamic Method: Routing a Compiled Kernel to Multiple Canvases

Because a WebGL context cannot change its host canvas after creation, you cannot swap the underlying canvas of an already compiled kernel. If you must output an existing compiled kernel to an arbitrary or dynamic canvas, use the 2D Canvas API's drawImage() method to transfer pixels from the kernel's internal canvas to your target canvas.

// Function to blit output to any arbitrary canvas
function renderToCustomCanvas(kernel, destinationCanvas) {
  // Execute the kernel so its internal canvas is updated
  kernel();

  const destContext = destinationCanvas.getContext('2d');
  
  // Ensure the target canvas matches dimensions
  destinationCanvas.width = kernel.canvas.width;
  destinationCanvas.height = kernel.canvas.height;

  // Draw the compiled kernel's WebGL canvas onto the destination canvas
  destContext.drawImage(kernel.canvas, 0, 0);
}

// Example usage with a dynamically selected canvas
const secondaryCanvas = document.getElementById('secondary-canvas');
renderToCustomCanvas(renderKernel, secondaryCanvas);

Summary of Options