BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

InisialisasiAdapter dan DeviceBuffersCreate BufferUpdate BufferTexturesCreate TextureSamplerWGSL (WebGPU Shading Language)Basic Vertex + Fragment ShaderUniform Buffer di ShaderTexture Sampling di ShaderCompute ShaderBind Group Layout dan Bind GroupRender PipelineRender PassCompute PipelineFull Triangle ExampleCanvas ResizeWebGPU vs WebGLDebuggingGlossary
WebGPUGraphicsGPUWGSL

WebGPU Cheat Sheet

Referensi cepat WebGPU API. Adapter, device, pipelines, buffers, textures, bind groups, shaders WGSL, dan compute passes. Perfect buat developer yang eksplor graphics programming modern.

JavaScript9 min read1.692 kata
Silakan login atau daftar untuk membaca cheat sheet ini.

#Inisialisasi

#Adapter dan Device

javascript
async function initWebGPU() {
  // 1. Check support
  if (!navigator.gpu) {
    throw new Error('WebGPU not supported in this browser');
  }
 
  // 2. Request adapter (GPU physical device)
  const adapter = await navigator.gpu.requestAdapter({
    powerPreference: 'high-performance', // atau 'low-power'
  });
 
  if (!adapter) {
    throw new Error('No suitable GPU adapter found');
  }
 
  // 3. Request device (logical GPU context)
  const device = await adapter.requestDevice({
    requiredFeatures: ['texture-compression-bc'],
    requiredLimits: {
      maxBufferSize: 1 << 28,
      maxStorageBufferBindingSize: 1 << 27,
    },
  });
 
  // 4. Configure canvas context
  const canvas = document.querySelector('canvas');
  const context = canvas.getContext('webgpu');
  const format = navigator.gpu.getPreferredCanvasFormat();
 
  context.configure({
    device,
    format,
    alphaMode: 'premultiplied', // atau 'opaque'
  });
 
  return { device, context, format, canvas };
}

#Buffers

#Create Buffer

javascript
// Vertex buffer (static data)
const vertexData = new Float32Array([
  // position (x, y, z), color (r, g, b, a)
   0.0,  0.5, 0.0,  1, 0, 0, 1,
  -0.5, -0.5, 0.0,  0, 1, 0, 1,
   0.5, -0.5, 0.0,  0, 0, 1, 1,
]);
 
const vertexBuffer = device.createBuffer({
  size: vertexData.byteLength,
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
 
// Write data ke buffer
device.queue.writeBuffer(vertexBuffer, 0, vertexData);
 
// Uniform buffer (updated each frame)
const uniformBuffer = device.createBuffer({
  size: 64, // 4x4 matrix (16 floats * 4 bytes)
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
 
// Storage buffer (GPU read/write)
const storageBuffer = device.createBuffer({
  size: 1000 * 4, // 1000 floats
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
 
// Read buffer back to CPU
const readBuffer = device.createBuffer({
  size: 1000 * 4,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
 
// Copy GPU buffer ke read buffer
encoder.copyBufferToBuffer(storageBuffer, 0, readBuffer, 0, 1000 * 4);
 
// Map untuk read
await readBuffer.mapAsync(GPUMapMode.READ);
const result = new Float32Array(readBuffer.getMappedRange());
console.log(result);
readBuffer.unmap();

#Update Buffer

javascript
// Update uniform setiap frame
const transformMatrix = new Float32Array(16);
// ... compute matrix ...
device.queue.writeBuffer(uniformBuffer, 0, transformMatrix);
 
// Dynamic buffer (aligned)
const dynamicOffset = 256; // harus multiple of 256
device.queue.writeBuffer(dynamicBuffer, dynamicOffset, data);

#Textures

#Create Texture

javascript
const texture = device.createTexture({
  size: [512, 512, 1],
  format: 'rgba8unorm',
  usage: GPUTextureUsage.TEXTURE_BINDING |
         GPUTextureUsage.COPY_DST |
         GPUTextureUsage.RENDER_ATTACHMENT,
});
 
// Write texture data dari image
const imageData = new Uint8Array(512 * 512 * 4);
// ... fill imageData ...
device.queue.writeTexture(
  { texture },
  imageData,
  { bytesPerRow: 512 * 4, rowsPerImage: 512 },
  [512, 512]
);
 
// Texture from image element
const img = new Image();
img.src = 'texture.jpg';
await img.decode();
const imageBitmap = await createImageBitmap(img);
 
device.queue.copyExternalImageToTexture(
  { source: imageBitmap },
  { texture },
  [img.width, img.height]
);

#Sampler

javascript
const sampler = device.createSampler({
  addressModeU: 'repeat',        // 'clamp-to-edge' | 'repeat' | 'mirror-repeat'
  addressModeV: 'repeat',
  addressModeW: 'repeat',
  magFilter: 'linear',           // 'nearest' | 'linear'
  minFilter: 'linear',
  mipmapFilter: 'linear',
  lodMinClamp: 0,
  lodMaxClamp: 32,
  compare: 'less',               // untuk shadow maps
});

#WGSL (WebGPU Shading Language)

#Basic Vertex + Fragment Shader

wgsl
// Vertex shader output struct
struct VertexOutput {
  @builtin(position) position: vec4f,
  @location(0) color: vec3f,
};
 
// Vertex shader
@vertex
fn vs_main(
  @location(0) pos: vec3f,
  @location(1) inColor: vec3f,
) -> VertexOutput {
  var output: VertexOutput;
  output.position = vec4f(pos, 1.0);
  output.color = inColor;
  return output;
}
 
// Fragment shader
@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4f {
  return vec4f(input.color, 1.0);
}

#Uniform Buffer di Shader

wgsl
// Define uniform struct
struct Uniforms {
  modelMatrix: mat4x4f,
  viewMatrix: mat4x4f,
  projectionMatrix: mat4x4f,
};
 
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
 
@vertex
fn vs_main(@location(0) pos: vec3f) -> @builtin(position) vec4f {
  let world = uniforms.modelMatrix * vec4f(pos, 1.0);
  let view = uniforms.viewMatrix * world;
  return uniforms.projectionMatrix * view;
}

#Texture Sampling di Shader

wgsl
@group(0) @binding(1) var tex: texture_2d<f32>;
@group(0) @binding(2) var samp: sampler;
 
@fragment
fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
  return textureSample(tex, samp, uv);
}

#Compute Shader

wgsl
@group(0) @binding(0) var<storage, read> inputData: array<f32>;
@group(0) @binding(1) var<storage, read_write> outputData: array<f32>;
 
@compute @workgroup_size(64)
fn cs_main(@builtin(global_invocation_id) id: vec3u) {
  let index = id.x;
  if (index >= arrayLength(&inputData)) {
    return;
  }
  // Simple: double each value
  outputData[index] = inputData[index] * 2.0;
}

#Bind Group Layout dan Bind Group

javascript
// Define bind group layout
const bindGroupLayout = device.createBindGroupLayout({
  entries: [
    {
      binding: 0,
      visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
      buffer: { type: 'uniform' },
    },
    {
      binding: 1,
      visibility: GPUShaderStage.FRAGMENT,
      texture: { sampleType: 'float' },
    },
    {
      binding: 2,
      visibility: GPUShaderStage.FRAGMENT,
      sampler: { type: 'filtering' },
    },
  ],
});
 
// Create bind group (assign actual resources)
const bindGroup = device.createBindGroup({
  layout: bindGroupLayout,
  entries: [
    { binding: 0, resource: { buffer: uniformBuffer } },
    { binding: 1, resource: texture.createView() },
    { binding: 2, resource: sampler },
  ],
});
 
// Pipeline layout
const pipelineLayout = device.createPipelineLayout({
  bindGroupLayouts: [bindGroupLayout],
});

#Render Pipeline

javascript
const shaderModule = device.createShaderModule({
  code: wgslCode, // WGSL string from above
});
 
const renderPipeline = device.createRenderPipeline({
  layout: pipelineLayout,
  vertex: {
    module: shaderModule,
    entryPoint: 'vs_main',
    buffers: [
      {
        arrayStride: 6 * 4, // 6 floats * 4 bytes
        attributes: [
          {
            shaderLocation: 0,
            offset: 0,
            format: 'float32x3', // position
          },
          {
            shaderLocation: 1,
            offset: 3 * 4,
            format: 'float32x3', // color
          },
        ],
      },
    ],
  },
  fragment: {
    module: shaderModule,
    entryPoint: 'fs_main',
    targets: [
      {
        format: presentationFormat,
        blend: {
          color: {
            srcFactor: 'src-alpha',
            dstFactor: 'one-minus-src-alpha',
            operation: 'add',
          },
          alpha: {
            srcFactor: 'one',
            dstFactor: 'one-minus-src-alpha',
            operation: 'add',
          },
        },
      },
    ],
  },
  primitive: {
    topology: 'triangle-list',  // 'point-list' | 'line-list' | 'line-strip' | 'triangle-list' | 'triangle-strip'
    cullMode: 'back',            // 'none' | 'front' | 'back'
    frontFace: 'ccw',            // 'ccw' | 'cw'
  },
  depthStencil: {
    format: 'depth24plus',
    depthWriteEnabled: true,
    depthCompare: 'less',
  },
});

#Render Pass

javascript
function render() {
  // Create command encoder
  const encoder = device.createCommandEncoder();
 
  // Create depth texture (per frame atau persistent)
  const depthTexture = device.createTexture({
    size: [canvas.width, canvas.height],
    format: 'depth24plus',
    usage: GPUTextureUsage.RENDER_ATTACHMENT,
  });
 
  // Begin render pass
  const renderPass = encoder.beginRenderPass({
    colorAttachments: [
      {
        view: context.getCurrentTexture().createView(),
        clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 },
        loadOp: 'clear',     // 'clear' | 'load'
        storeOp: 'store',    // 'store' | 'discard'
      },
    ],
    depthStencilAttachment: {
      view: depthTexture.createView(),
      depthClearValue: 1.0,
      depthLoadOp: 'clear',
      depthStoreOp: 'store',
    },
  });
 
  // Set pipeline dan resources
  renderPass.setPipeline(renderPipeline);
  renderPass.setBindGroup(0, bindGroup);
  renderPass.setVertexBuffer(0, vertexBuffer);
  renderPass.setIndexBuffer(indexBuffer, 'uint16');
 
  // Draw
  renderPass.drawIndexed(3);        // indexed draw
  // atau
  renderPass.draw(3, 1, 0, 0);     // vertexCount, instanceCount, firstVertex, firstInstance
 
  // Multiple instances (instanced rendering)
  renderPass.draw(3, 100);          // 100 instances
 
  // End pass
  renderPass.end();
 
  // Submit
  device.queue.submit([encoder.finish()]);
 
  // Cleanup depth texture
  depthTexture.destroy();
}
 
requestAnimationFrame(render);

#Compute Pipeline

javascript
const computePipeline = device.createComputePipeline({
  layout: device.createPipelineLayout({
    bindGroupLayouts: [computeBindGroupLayout],
  }),
  compute: {
    module: shaderModule,
    entryPoint: 'cs_main',
  },
});
 
function compute() {
  const encoder = device.createCommandEncoder();
  const computePass = encoder.beginComputePass();
 
  computePass.setPipeline(computePipeline);
  computePass.setBindGroup(0, computeBindGroup);
 
  // Dispatch workgroups
  // workgroup_size(64) di shader = 64 threads per workgroup
  // total threads = workgroupCountX * 64
  const dataSize = 10000;
  const workgroupCount = Math.ceil(dataSize / 64);
  computePass.dispatchWorkgroups(workgroupCount);
 
  computePass.end();
 
  device.queue.submit([encoder.finish()]);
}

#Full Triangle Example

javascript
async function main() {
  const { device, context, format, canvas } = await initWebGPU();
 
  // Vertex data
  const vertices = new Float32Array([
     0.0,  0.5,  1, 0, 0,
    -0.5, -0.5,  0, 1, 0,
     0.5, -0.5,  0, 0, 1,
  ]);
 
  const vertexBuffer = device.createBuffer({
    size: vertices.byteLength,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
  });
  device.queue.writeBuffer(vertexBuffer, 0, vertices);
 
  // Shader
  const shader = device.createShaderModule({
    code: `
      struct VOut {
        @builtin(position) pos: vec4f,
        @location(0) color: vec3f,
      };
 
      @vertex
      fn vs(@location(0) p: vec2f, @location(1) c: vec3f) -> VOut {
        var o: VOut;
        o.pos = vec4f(p, 0.0, 1.0);
        o.color = c;
        return o;
      }
 
      @fragment
      fn fs(@location(0) c: vec3f) -> @location(0) vec4f {
        return vec4f(c, 1.0);
      }
    `,
  });
 
  // Pipeline
  const pipeline = device.createRenderPipeline({
    layout: 'auto',
    vertex: {
      module: shader,
      entryPoint: 'vs',
      buffers: [{
        arrayStride: 20,
        attributes: [
          { shaderLocation: 0, offset: 0, format: 'float32x2' },
          { shaderLocation: 1, offset: 8, format: 'float32x3' },
        ],
      }],
    },
    fragment: {
      module: shader,
      entryPoint: 'fs',
      targets: [{ format }],
    },
    primitive: { topology: 'triangle-list' },
  });
 
  // Render
  function frame() {
    const encoder = device.createCommandEncoder();
    const pass = encoder.beginRenderPass({
      colorAttachments: [{
        view: context.getCurrentTexture().createView(),
        clearValue: { r: 0, g: 0, b: 0, a: 1 },
        loadOp: 'clear',
        storeOp: 'store',
      }],
    });
    pass.setPipeline(pipeline);
    pass.setVertexBuffer(0, vertexBuffer);
    pass.draw(3);
    pass.end();
    device.queue.submit([encoder.finish()]);
    requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
}
 
main();

#Canvas Resize

javascript
function handleResize() {
  const dpr = Math.min(window.devicePixelRatio, 2);
  canvas.width = canvas.clientWidth * dpr;
  canvas.height = canvas.clientHeight * dpr;
  // Context auto-reconfigure dengan size baru
}
 
window.addEventListener('resize', handleResize);
handleResize(); // initial

#WebGPU vs WebGL

AspekWebGPUWebGL 2.0
Shading LanguageWGSLGLSL
APIModern, explicitLegacy, stateful
Compute ShadersYa (native)Tidak (webgl-compute hack)
PerformanceLebih baikBaik
Bind ModelBind Groups (explicit)Global state
ThreadingYa (Web Workers)Tidak
Browser SupportChrome 113+, Edge, Safari 18+Semua modern browsers
MaturityEmergingMature

#Debugging

javascript
// Error scope
device.pushErrorScope('validation');
// ... GPU operations ...
const error = await device.popErrorScope();
if (error) {
  console.error('GPU error:', error.message);
}
 
// Uncaptured error handler
device.addEventListener('uncapturederror', (event) => {
  console.error('Uncaptured GPU error:', event.error.message);
});
 
// Label objects untuk debugging
const buffer = device.createBuffer({
  label: 'Vertex Buffer',
  // ...
});
 
// Validate shader compilation
const shader = device.createShaderModule({ code });
const info = await shader.getCompilationInfo();
if (info.messages.length > 0) {
  info.messages.forEach(msg => {
    console.log(`${msg.type}: ${msg.message} (line ${msg.lineNum})`);
  });
}

#Glossary

  • Adapter: Representasi physical GPU. Dipilih via requestAdapter().
  • Device: Logical GPU context. Setiap app butuh device sendiri.
  • Queue: Tempat submit GPU commands. device.queue untuk operasi write.
  • Pipeline: Pre-compiled state object (render atau compute). Mahal di-create, murah di-use.
  • Bind Group: Binding antara shader resources (buffers, textures) dan actual GPU resources.
  • Buffer: Blok memory di GPU. Punya usage flags (vertex, uniform, storage, dst).
  • Texture: 2D/3D image data di GPU. Punya format dan usage flags.
  • Sampler: Object yang define cara sample texture (filtering, wrapping).
  • WGSL: WebGPU Shading Language. Bahasa shader untuk WebGPU.
  • Workgroup: Unit eksekusi di compute shader. workgroup_size define threads per workgroup.
  • Command Encoder: Object yang record GPU commands untuk kemudian di-submit sekaligus.
  • Render Pass: Session yang render ke texture targets (color attachments, depth attachment).
  • Compute Pass: Session yang run compute shaders tanpa rendering output.
  • Bind Group Layout: Template untuk bind group. Define tipe resource per binding slot.

Baca Cheat Sheet Lengkap

Login atau daftar akun gratis untuk membaca cheat sheet ini.

LoginDaftar Gratis
Share: