You are currently viewing WebGPU for Beginners: How to Run GPU-Powered Code in the Browser

WebGPU for Beginners: How to Run GPU-Powered Code in the Browser


JavaScript normally runs on the CPU. That works well for most websites, but some tasks involve processing large amounts of data or rendering complex graphics. For those jobs, a GPU can be much more useful.

Until fairly recently, using the GPU from a website usually meant working with WebGL or relying on a library that handled the complicated parts for you. WebGPU gives developers a more modern way to work with graphics hardware directly from the browser.

And it isn’t limited to drawing 3D objects. WebGPU can also run calculations on the GPU using compute shaders. That makes it interesting for image processing, simulations, data visualization and certain machine-learning workloads.

In this WebGPU for beginners tutorial, we’ll start with the basic concepts and then run a small JavaScript example that sends numbers to the GPU, processes them with a compute shader and reads the results back.

What Is WebGPU?

WebGPU is a browser API that allows web applications to use a device’s GPU for rendering graphics and performing computations.

It provides access to concepts found in modern graphics APIs, such as command buffers, rendering pipelines, compute pipelines and GPU resources.

The difference from ordinary JavaScript is where the work happens. Your JavaScript code prepares the resources and commands, while specially written shader code runs on the GPU.

For example, imagine applying the same image filter to millions of pixels. A GPU is designed to perform many similar operations in parallel. WebGPU gives browser applications a way to make use of that hardware.

That doesn’t mean every JavaScript calculation becomes faster when moved to WebGPU. Preparing buffers, transferring data and reading results introduces overhead. For small tasks, normal JavaScript may still be the simpler and faster choice.

WebGPU vs WebGL: What’s Different?

If you’ve experimented with browser graphics before, you’ve probably come across WebGL.

WebGL is mainly built around rendering graphics. WebGPU also handles rendering, but it adds a more modern resource model and direct support for general-purpose GPU computation.

FeatureWebGLWebGPU
Main purposeGPU-accelerated graphicsGraphics and general-purpose computation
Shader languageGLSL ESWGSL
Compute shadersNo native compute-shader pipelineBuilt-in compute pipelines
GPU resourcesTraditional graphics-oriented APIExplicit buffers, bind groups and pipelines
Browser compatibilityMore establishedStill varies by browser and platform

WebGL remains useful, particularly when broad compatibility matters. There is no reason to rewrite an existing WebGL project solely because WebGPU is newer.

But if you’re starting an application that needs substantial GPU computation, WebGPU is worth investigating.

What Can You Build With WebGPU?

Games and 3D applications are obvious examples, but they’re not the only possibilities.

  • Image processing: Apply filters, color adjustments or transformations to large images.
  • Browser games: Render complex scenes and run GPU-based simulations.
  • Data visualization: Process or display large datasets using GPU acceleration.
  • Scientific applications: Run suitable parallel computations and simulations.
  • Machine learning: Accelerate supported operations when using WebGPU-enabled frameworks or runtimes.

If you’ve explored creative coding before, this should sound familiar. The difference is that WebGPU gives JavaScript applications access to a newer generation of GPU features. Our article on programming languages for creative coding covers some of the broader options for this kind of work.

Before You Start: Browser and Hardware Support

WebGPU has expanded across major browsers, but you shouldn’t assume every visitor can use it.

Support depends on the browser, operating system, graphics hardware and driver configuration. A browser may expose WebGPU on one computer and not another.

WebGPU also requires a secure context. Use HTTPS when hosting a demo online, or run your example on localhost during development.

For the latest compatibility details, check Can I Use: WebGPU or MDN’s browser compatibility table. Both are more useful than relying on a browser-support list that may be outdated by the time you read it.

Check Whether Your Browser Supports WebGPU

Open your browser’s developer console and run:

console.log('WebGPU available:', !!navigator.gpu);
JavaScript

If it prints true, the browser exposes the WebGPU entry point.

That doesn’t guarantee you can create a usable GPU device. You still need to request an adapter, and that request can fail even when navigator.gpu exists.

Your First WebGPU Program: Run a Compute Shader

Let’s make a small program that runs an actual calculation on the GPU.

We’ll send four numbers to a GPU buffer, multiply each number by two using a compute shader, and read the result back into JavaScript.

The input will be:

[1, 2, 3, 4]
JavaScript

And the expected output is:

[2, 4, 6, 8]
JavaScript

Obviously, JavaScript could handle this example in one line. That’s intentional. The calculation is simple so we can focus on how WebGPU works without introducing complicated mathematics.

Step 1: Create an HTML File

Create a new folder and add a file named index.html. Paste the following code into it.

This is a complete standalone example. You don’t need a JavaScript framework or a WebGPU library to run it.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My First WebGPU Example</title>
</head>
<body>
  <h1>My First WebGPU Compute Shader</h1>
  <pre id="result">Running...</pre>

  <script type="module">
    const output = document.getElementById('result');

    async function runWebGPU() {
      if (!navigator.gpu) {
        throw new Error('WebGPU is not available in this browser.');
      }

      const adapter = await navigator.gpu.requestAdapter();

      if (!adapter) {
        throw new Error('No suitable GPU adapter was found.');
      }

      const device = await adapter.requestDevice();

      const input = new Float32Array([1, 2, 3, 4]);

      // Create a buffer that the GPU shader can access.
      const workBuffer = device.createBuffer({
        size: input.byteLength,
        usage: GPUBufferUsage.STORAGE |
               GPUBufferUsage.COPY_DST |
               GPUBufferUsage.COPY_SRC
      });

      device.queue.writeBuffer(workBuffer, 0, input);

      // This buffer lets JavaScript read the result.
      const readBuffer = device.createBuffer({
        size: input.byteLength,
        usage: GPUBufferUsage.COPY_DST |
               GPUBufferUsage.MAP_READ
      });

      const shader = device.createShaderModule({
        code: `
          @group(0) @binding(0)
          var<storage, read_write> numbers: array<f32>;

          @compute @workgroup_size(64)
          fn main(@builtin(global_invocation_id) id: vec3u) {
            let i = id.x;

            if (i >= arrayLength(&numbers)) {
              return;
            }

            numbers[i] = numbers[i] * 2.0;
          }
        `
      });

      const pipeline = device.createComputePipeline({
        layout: 'auto',
        compute: {
          module: shader,
          entryPoint: 'main'
        }
      });

      const bindGroup = device.createBindGroup({
        layout: pipeline.getBindGroupLayout(0),
        entries: [
          {
            binding: 0,
            resource: { buffer: workBuffer }
          }
        ]
      });

      const encoder = device.createCommandEncoder();
      const pass = encoder.beginComputePass();

      pass.setPipeline(pipeline);
      pass.setBindGroup(0, bindGroup);
      pass.dispatchWorkgroups(Math.ceil(input.length / 64));
      pass.end();

      encoder.copyBufferToBuffer(
        workBuffer,
        0,
        readBuffer,
        0,
        input.byteLength
      );

      device.queue.submit([encoder.finish()]);

      await readBuffer.mapAsync(GPUMapMode.READ);

      const result = Array.from(
        new Float32Array(readBuffer.getMappedRange())
      );

      readBuffer.unmap();

      workBuffer.destroy();
      readBuffer.destroy();

      output.textContent = 'Result: ' + JSON.stringify(result);
      console.log(result);
    }

    runWebGPU().catch(error => {
      console.error(error);
      output.textContent = error.message;
    });
  </script>
</body>
</html>
HTTP

Step 2: Run the Example on Localhost

Instead of opening the HTML file directly from your file manager, serve it through a local development server. This avoids problems with browser security restrictions and matches the environment you’ll normally use for web development.

If Python is installed, open a terminal in the project folder and run:

python3 -m http.server 8000
PowerShell

On Windows, the command may be:

py -m http.server 8000
PowerShell

Then open:

http://localhost:8000
HTTP

If your browser and hardware support WebGPU, the page should display:

Result: [2,4,6,8]
JavaScript

If it displays an error instead, check the browser console. WebGPU support, graphics drivers and security settings are the first things to investigate.

Understanding the WebGPU Code

There is quite a lot happening in that example just to multiply four numbers. Once you understand the main pieces, though, the structure starts to make sense.

The Adapter and Device

The first important lines are:

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
JavaScript

The adapter represents a suitable GPU implementation selected by the browser. The device gives your application the interface it needs to create GPU resources and submit work.

A real application should handle the possibility that no adapter or device is available. Our complete example checks for a missing adapter before continuing.

GPU Buffers

A GPU buffer stores data that GPU operations can use.

In our example, workBuffer contains the numbers the shader will modify. We also create readBuffer because GPU data isn’t accessed in exactly the same way as an ordinary JavaScript array.

After the calculation finishes, WebGPU copies the result into a buffer JavaScript can read.

What Is WGSL?

The code that actually runs on the GPU is written in WebGPU Shading Language (WGSL).

Here’s the important part of our shader:

@group(0) @binding(0)
var<storage, read_write> numbers: array<f32>;

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3u) {
  let i = id.x;

  if (i >= arrayLength(&numbers)) {
    return;
  }

  numbers[i] = numbers[i] * 2.0;
}
JavaScript

The shader reads a value from the array, multiplies it by two and writes the result back.

The global_invocation_id tells each invocation which array position to process.

We use a workgroup size of 64, even though the input contains only four numbers. The bounds check prevents the extra invocations from accessing elements that don’t exist.

Google’s first WebGPU application tutorial covers workgroups and compute shaders in more detail.

The Compute Pipeline

A compute pipeline describes the shader WebGPU should execute and how its resources are organized.

The bind group connects our GPU buffer to the shader’s declared binding. Without it, the shader wouldn’t know which resource to read and write.

Finally, the compute pass records the work we want the GPU to execute. When we submit the command buffer, the GPU processes the data.

WebGPU Fundamentals has a useful introduction to buffers, bind groups and compute pipelines if you want to explore the individual steps further.

Why Not Just Use JavaScript?

For our four-number example, normal JavaScript would be much better:

const numbers = [1, 2, 3, 4];
const result = numbers.map(number => number * 2);

console.log(result);
JavaScript

It’s shorter, easier to debug and doesn’t need a GPU at all.

WebGPU becomes interesting when the amount of suitable parallel work is large enough to justify the overhead of preparing buffers and communicating with the GPU.

That might mean processing large images, simulating particles or running batches of mathematical operations. Even then, performance depends on the hardware, the algorithm and how much data needs to move between CPU and GPU memory.

Don’t assume GPU code is faster until you’ve measured it using a realistic workload.

Rendering Graphics With WebGPU

Our example uses a compute pipeline, but WebGPU can also render graphics into a canvas.

For rendering, the basic process is a little different. You create a canvas context, configure its output format, build a render pipeline and submit draw commands.

WebGPU uses vertex and fragment shaders for graphics rendering. Compute shaders are used for general calculations and don’t directly draw pixels to a canvas.

If your goal is to create a 3D application, you’ll eventually need to learn about vertices, textures, coordinate systems and rendering pipelines.

You don’t necessarily need to build all of that yourself. Libraries and game engines can provide a higher-level way to work with GPU rendering.

For readers exploring graphics programming more broadly, our beginner’s guide to game development covers the wider choices involved in getting started.

Common Problems When Trying WebGPU

navigator.gpu Is Undefined

This usually means WebGPU isn’t exposed in the current environment. Check that you’re using a compatible browser and a secure context. Hardware support or browser configuration may also be responsible.

requestAdapter() Returns null

The browser couldn’t provide a suitable GPU adapter. This can happen because of hardware limitations, driver issues, unsupported configurations or browser restrictions.

Try updating the browser and graphics drivers, then check the browser’s graphics diagnostics. Don’t assume the API must work simply because the computer has a GPU.

Shader Validation Errors

WGSL is a separate language with its own syntax and rules. JavaScript syntax doesn’t automatically work inside a WGSL shader.

Check shader compilation messages, binding declarations, data types and pipeline configuration. Even small differences in types can cause validation errors.

The GPU Calculation Is Slower Than Expected

A GPU isn’t a shortcut for every performance problem. Small calculations can spend more time on setup and data transfers than on the actual computation.

Try a larger, realistic workload and measure the complete operation, including data transfers and readback, before judging whether WebGPU is a useful improvement.

Should You Use WebGPU in Production?

That depends on what you’re building and which browsers your audience uses.

For a graphics-heavy application where GPU processing is central to the experience, WebGPU may be worth the additional development effort.

For a standard business website or blog, you probably don’t need it.

Because support is still inconsistent across some environments, production applications should detect WebGPU availability and provide a fallback where possible. Depending on the task, that could mean a WebGL renderer, a simpler JavaScript implementation or a message explaining that the feature requires compatible hardware.

It’s also worth testing on actual devices. A successful run on a powerful desktop doesn’t tell you how the same application will behave on an older laptop or mobile phone.

Where to Learn More About WebGPU

Once the compute example makes sense, these are good places to continue:

Leave a Reply