How to Use GPU.js Inside Web Workers

GPU.js allows developers to accelerate complex JavaScript computations by compiling code into WebGL shader language to run on the GPU. While running heavy operations on the GPU is fast, compiling kernels and transferring large datasets can still cause micro-stutters on the main browser thread. By running GPU.js inside a Web Worker, you can fully isolate heavy computations and data orchestration, ensuring that the user interface remains completely fluid and responsive.

Can GPU.js Run in a Web Worker?

Yes, GPU.js can be executed inside a Web Worker. Historically, WebGL operations required access to the DOM via an HTML <canvas> element, which is unavailable in worker threads. However, modern browsers support the OffscreenCanvas API, which allows canvas rendering contexts—including WebGL—to run independently of the DOM inside Web Workers. GPU.js leverages OffscreenCanvas to generate WebGL contexts and run kernels outside the main UI thread.

If a browser or device does not support WebGL inside a worker context, GPU.js can automatically fall back to its CPU mode. Even in CPU fallback mode, executing the task inside a Web Worker prevents the main thread from freezing.

How to Implement GPU.js in a Web Worker

To run GPU.js inside a worker, you need to load the library in your worker script, instantiate the GPU object, define your kernel, and use the standard worker messaging interface (postMessage and onmessage) to send and receive data.

1. The Web Worker Script (worker.js)

Inside your dedicated worker script, import the GPU.js library using importScripts() or ES module imports, depending on your build setup.

// worker.js
importScripts('https://cdnjs.cloudflare.com/ajax/libs/gpu.js/gpu-browser.min.js');

const gpu = new GPU();

// Create a GPU kernel
const multiplyMatrix = gpu.createKernel(function(a, b, size) {
    let sum = 0;
    for (let i = 0; i < 512; i++) {
        sum += a[this.thread.y][i] * b[i][this.thread.x];
    }
    return sum;
}).setOutput([512, 512]);

self.onmessage = function(e) {
    const { matrixA, matrixB, size } = e.data;

    // Run the computation on the GPU
    const result = multiplyMatrix(matrixA, matrixB, size);

    // Send the result back to the main thread
    self.postMessage(result);
};

2. The Main Thread Script (main.js)

On the main thread, spawn the worker and communicate with it asynchronously.

// main.js
const worker = new Worker('worker.js');

// Prepare large data arrays
const matrixA = generateMatrix(512);
const matrixB = generateMatrix(512);

// Send data to the worker
worker.postMessage({ matrixA, matrixB, size: 512 });

// Receive the computed output without UI interruption
worker.onmessage = function(e) {
    const computedData = e.data;
    console.log('Calculation complete:', computedData);
};

function generateMatrix(size) {
    return Array.from({ length: size }, () =>
        Array.from({ length: size }, () => Math.random())
    );
}

Key Considerations