---
id: frontend-webgpu-patterns
title: WebGPU — Browser GPU 컴퓨팅 / 그래픽
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [frontend, webgpu, gpu, vibe-coding]
tech_stack: { language: "TS / WGSL", applicable_to: ["Frontend"] }
applied_in: []
aliases: [WebGPU, WGSL, compute shader, render pipeline, GPU buffer, browser GPU]
---

# WebGPU

> WebGL 후속. **Modern GPU API + compute shader**. 그래픽 + GPU compute (ML inference, simulation). 2024+ Chrome / Edge / Safari / Firefox 지원.

## 📖 핵심 개념
- Adapter / Device: GPU handle.
- Shader: WGSL (WebGPU Shading Language).
- Pipeline: render or compute.
- Buffer: GPU 메모리.

## 💻 코드 패턴

### Setup
```ts
if (!navigator.gpu) throw new Error('WebGPU not supported');

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

### 캔버스 setup
```ts
const canvas = document.querySelector('canvas')!;
const ctx = canvas.getContext('webgpu')!;
const format = navigator.gpu.getPreferredCanvasFormat();

ctx.configure({ device, format, alphaMode: 'premultiplied' });
```

### 간단 render — 색칠된 삼각형
```ts
const shader = device.createShaderModule({
  code: `
    @vertex
    fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
      var p = array<vec2f, 3>(
        vec2f(0.0, 0.5),
        vec2f(-0.5, -0.5),
        vec2f(0.5, -0.5)
      );
      return vec4f(p[i], 0.0, 1.0);
    }

    @fragment
    fn fs() -> @location(0) vec4f {
      return vec4f(1.0, 0.5, 0.0, 1.0);
    }
  `,
});

const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: { module: shader, entryPoint: 'vs' },
  fragment: { module: shader, entryPoint: 'fs', targets: [{ format }] },
});

function render() {
  const encoder = device.createCommandEncoder();
  const pass = encoder.beginRenderPass({
    colorAttachments: [{
      view: ctx.getCurrentTexture().createView(),
      clearValue: [0, 0, 0, 1],
      loadOp: 'clear',
      storeOp: 'store',
    }],
  });
  pass.setPipeline(pipeline);
  pass.draw(3);
  pass.end();
  device.queue.submit([encoder.finish()]);
}

render();
```

### Compute shader (matrix multiply)
```ts
const shader = device.createShaderModule({
  code: `
    @group(0) @binding(0) var<storage, read> a: array<f32>;
    @group(0) @binding(1) var<storage, read> b: array<f32>;
    @group(0) @binding(2) var<storage, read_write> result: array<f32>;
    
    @compute @workgroup_size(64)
    fn main(@builtin(global_invocation_id) id: vec3u) {
      let i = id.x;
      result[i] = a[i] * b[i] + 1.0;
    }
  `,
});

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

const N = 1024 * 1024;
const aArr = new Float32Array(N).fill(2);
const bArr = new Float32Array(N).fill(3);

const aBuf = device.createBuffer({ size: aArr.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
const bBuf = device.createBuffer({ size: bArr.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
const resultBuf = device.createBuffer({ size: N * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });

device.queue.writeBuffer(aBuf, 0, aArr);
device.queue.writeBuffer(bBuf, 0, bArr);

const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [
    { binding: 0, resource: { buffer: aBuf } },
    { binding: 1, resource: { buffer: bBuf } },
    { binding: 2, resource: { buffer: resultBuf } },
  ],
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(N / 64));
pass.end();

// CPU 로 read back
const readBuf = device.createBuffer({ size: N * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
encoder.copyBufferToBuffer(resultBuf, 0, readBuf, 0, N * 4);
device.queue.submit([encoder.finish()]);

await readBuf.mapAsync(GPUMapMode.READ);
const result = new Float32Array(readBuf.getMappedRange().slice(0));
readBuf.unmap();
console.log(result[0]);  // 7
```

### Texture / image
```ts
const img = await createImageBitmap(await fetch('/image.jpg').then(r => r.blob()));

const tex = device.createTexture({
  size: [img.width, img.height],
  format: 'rgba8unorm',
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
});

device.queue.copyExternalImageToTexture({ source: img }, { texture: tex }, [img.width, img.height]);
```

### ML — Transformers.js (WebGPU backend)
```ts
import { pipeline, env } from '@xenova/transformers';

env.backends.onnx.preferredBackend = 'webgpu';

const pipe = await pipeline('text-generation', 'Xenova/Llama-3.2-1B-Instruct');
const out = await pipe('Hello', { max_new_tokens: 50 });
```

→ Browser 안 LLM 빠름 (WebGPU 가 WASM 보다 5-10x).

### Compute — image filter
```wgsl
@group(0) @binding(0) var inputTex: texture_2d<f32>;
@group(0) @binding(1) var outputTex: texture_storage_2d<rgba8unorm, write>;

@compute @workgroup_size(8, 8)
fn blur(@builtin(global_invocation_id) id: vec3u) {
    let coord = vec2i(id.xy);
    var color = vec4f(0.0);
    for (var dx = -2; dx <= 2; dx++) {
      for (var dy = -2; dy <= 2; dy++) {
        color += textureLoad(inputTex, coord + vec2i(dx, dy), 0);
      }
    }
    color /= 25.0;
    textureStore(outputTex, coord, color);
}
```

### 3D rendering
```ts
// Three.js + WebGPU renderer
import { WebGPURenderer } from 'three/examples/jsm/renderers/webgpu/WebGPURenderer.js';

const renderer = new WebGPURenderer({ canvas });
await renderer.init();
// 일반 Three.js scene
```

### Babylon.js
```ts
import { Engine, WebGPUEngine } from '@babylonjs/core';

const engine = new WebGPUEngine(canvas);
await engine.initAsync();
```

### Fallback (WebGL / WASM)
```ts
async function getRenderer() {
  if (navigator.gpu) {
    try {
      const adapter = await navigator.gpu.requestAdapter();
      if (adapter) return 'webgpu';
    } catch {}
  }
  if ('WebGL2RenderingContext' in window) return 'webgl2';
  return 'cpu';
}
```

### Performance tips
- Workgroup size = multiple of 32 (보통 64).
- Buffer alignment 16 byte.
- 큰 buffer 보다 small batch.
- Texture > raw buffer for spatial.
- writeBuffer 보다 staging buffer + queue.

### 디버그
```
Chrome: chrome://gpu, Performance tab, GPU profiler.
Spector.js — WebGL/WebGPU frame capture.
```

### Limitations (2026)
```
+ Major browsers 지원 (Chrome 113+, Safari 17.4+, Firefox 141+).
- Mobile: 일부 device 만.
- Compute shader = 안전 (서버 모델 download 가능).
- Atomic operations 일부 한정.
```

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 3D 새 프로젝트 | WebGPU (Three.js / Babylon) |
| 기존 WebGL | 점진 마이그레이션 |
| ML inference | Transformers.js + WebGPU |
| GPU compute | WebGPU compute shader |
| 옛 browser 지원 | WebGL fallback |
| Game | Unity WebGPU / 자체 |

## ❌ 안티패턴
- **No fallback**: 옛 browser fail.
- **모든 frame writeBuffer 큰 data**: bandwidth. staging.
- **CPU read back 매 frame**: 느림. GPU keep.
- **너무 작은 dispatch**: launch overhead.
- **Memory leak (destroy 안 함)**: GPU resources.
- **Float64 가정**: WebGPU = float32.
- **Adapter / device 매번 request**: cache.

## 🤖 LLM 활용 힌트
- Compute shader = ML / image / simulation 강력.
- Three.js / Babylon WebGPU renderer 가 가장 단순 시작.
- Transformers.js + WebGPU = browser LLM.

## 🔗 관련 문서
- [[Frontend_Three_R3F]]
- [[Frontend_WASM_Integration]]
- [[AI_Local_LLM_Inference]]
