www.xbdev.net
xbdev - software development
Wednesday September 16, 2026
Home | Contact | Support | WebGPU Graphics and Compute ... | WebGPU.. Games, Tutorials, Demos, Projects, and Code.....
     
 

WebGPU..

Games, Tutorials, Demos, Projects, and Code.....

 


Color Triangle (Rainbow)


Taking the basic triangle and adding color! In fact, this example does a bit more than add color - it also uses depth buffer! The depth buffer can't really be appreciated here, since we're only drawing a single triangle - however, when we start to ramp up things - it's be seen as a godsent!


Color triangle on screen
Color triangle on screen


Functions Used: setVertexBuffer(), setIndexBuffer(), drawIndexed(), createBuffer(), getMappedRange(), getContext(), requestAdapter(), getPreferredCanvasFormat(), createCommandEncoder(), beginRenderPass(), setPipeline(), draw(), end(), submit(), getCurrentTexture(), createView(), createShaderModule()

This triangle version has considerably more going on than the previous 50 line version. We are now creating a vertex and index buffer to represent the triangle geometry. The vertex data also has color information (not just positions).

The color information when it's passed from the vertex to the fragment shader is interpolated - that why you get those pretty rainbow colors and not just hard red, green and blue.

The code uses vanilla Javascript - so it doesn't depend on any libraries or external resources.



/*
    WebGPU Example - Color Triangle
    Simple uncluttered example (no libraries)
    
    Key details:
    - Index buffer (faces), vertices, color, depth buffer, shaders, 
*/
let canvas = document.createElement('canvas');
document.body.appendChild( canvas );
canvas.width  = canvas.height = 512;

const adapter = await navigator.gpu.requestAdapter();
const device  = await adapter.requestDevice();
const context = canvas.getContext('webgpu');

const presentationSize   = [ canvas.width,   
                             canvas.height ] 

const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

context.configure({ device : device, alphaMode: "opaque", // old way compositingAlphaMode: "opaque"
                    format : presentationFormat,
                    size   : presentationSize });
const vertWGSL = `
struct VSOut {
    @builtin(position) Position: vec4<f32>,
    @location(0)       color   : vec3<f32>,
};

@vertex
fn main(@location(0) inPos  : vec3<f32>,
        @location(1) inColor: vec3<f32>) -> VSOut 
{
    var vsOut: VSOut;
    vsOut.Position = vec4<f32>(inPos, 1.0);
    vsOut.color    = inColor;
    return vsOut;
}
`;

const fragWGSL = `
@fragment
fn main(@location(0) inColor: vec3<f32>) -> @location(0) vec4<f32> 
{
    return vec4<f32>(inColor, 1.0);
}
`;

const positions = new Float32Array([-1.0, -1.0, 0.0,   // Position Vertex Buffer Data
                                     1.0, -1.0, 0.0,
                                     0.0,  1.0, 0.0 ]);
const colors    = new Float32Array([ 1.0, 0.0, 0.0,    // Color Vertex Buffer Data
                                     0.0, 1.0, 0.0, 
                                     0.0, 0.0, 1.0  ]);
const indices   = new Uint16Array( [ 0, 1, 2 ]);       // Index Buffer Data

const createBuffer = (arrData, usage) => {
  const buffer = device.createBuffer({ size            : ((arrData.byteLength + 3) & ~3),
                                       usage           : usage,
                                       mappedAtCreation: true  });
  if ( arrData instanceof Float32Array ) 
  { (new Float32Array(buffer.getMappedRange())).set(arrData) }
  else 
  { (new Uint16Array (buffer.getMappedRange())).set(arrData) }
  buffer.unmap();
  return buffer;
}

// Declare buffer handles (GPUBuffer)
var positionBuffer = createBuffer(positions, GPUBufferUsage.VERTEX);
var colorBuffer    = createBuffer(colors,    GPUBufferUsage.VERTEX);
var indexBuffer    = createBuffer(indices,   GPUBufferUsage.INDEX);

const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex:    { module     : device.createShaderModule({code   : vertWGSL }),
               entryPoint : 'main',
               buffers    : [ { arrayStride: 12, attributes: [{ shaderLocation: 0,
                                                                format: "float32x3",
                                                                offset: 0  }]         },
                              { arrayStride: 12, attributes: [{ shaderLocation: 1,
                                                                format: "float32x3",
                                                                offset: 0  }]         }
    ]
  },
  fragment:  { module     : device.createShaderModule({ code  : fragWGSL }),
               entryPoint : 'main',
               targets    : [ {format: presentationFormat } ],
  },
  primitive: {
    topology: "triangle-list",
    cullMode: 'back'
  },
  depthStencil: {
    format: "depth24plus",
    depthWriteEnabled: true,
    depthCompare: "less"
  }
});

const depthTexture = device.createTexture({
  size: [canvas.width, canvas.height, 1],
  format: "depth24plus",
  usage:  GPUTextureUsage.RENDER_ATTACHMENT
})

const renderPassDescription = {
  colorAttachments: [{
    view: context.getCurrentTexture().createView(),
    loadOp:"clear", clearValue: [0, 0.5, 0.5, 1], // clear screen to color
    storeOp: 'store'
  }],
  depthStencilAttachment: {
    view: depthTexture.createView(),
    depthLoadOp:"clear", depthClearValue: 1,
    depthStoreOp: "store",
  }
};

function frame() 
{
  renderPassDescription.colorAttachments[0].view = context.getCurrentTexture().createView();
  const commandEncoder = device.createCommandEncoder();
  const renderPass = commandEncoder.beginRenderPass(renderPassDescription);

  renderPass.setPipeline(pipeline);
  renderPass.setVertexBuffer(0, positionBuffer);
  renderPass.setVertexBuffer(1, colorBuffer);
  renderPass.setIndexBuffer(indexBuffer, 'uint16');
  renderPass.drawIndexed(3,1);
  renderPass.end();

  device.queue.submit([commandEncoder.finish()]);
} 

frame();

// only update when moving the mouse (modified to update all time)
onmousemove = function() {
  frame();
}

console.log('ready...');


Resources and Links


• Live Example in WebGPU Lab [LINK]




































WebGPU by Example: Fractals, Image Effects, Ray-Tracing, Procedural Geometry, 2D/3D, Particles, Simulations WebGPU Compute graphics and animations using the webgpu api 12 week course kenwright learn webgpu api kenwright programming compute and graphics applications with html5 and webgpu api kenwright real-time 3d graphics with webgpu kenwright webgpu api develompent a quick start guide kenwright webgpu by example 2022 kenwright webgpu gems kenwright webgpu interactive compute and graphics visualization cookbook kenwright wgsl webgpu shading language cookbook kenwright wgsl webgpugems shading language cookbook kenwright



 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2026 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.