Skip to content

WebGL Recipes

These examples use familiar browser WebGL patterns on top of @nativescript/canvas.

Visual output preview

Preview of WebGL recipe output

The baseline expected output is a stable triangle render, then iterative improvements through shader changes, uniforms, blending, depth testing, and viewport-resize handling.

Shared setup

ts
let canvas;
let gl;

export function canvasReady(args) {
  canvas = args.object;
  gl = canvas.getContext('webgl');

  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}

1. Clear color and depth buffers

ts
gl.clearColor(0.08, 0.1, 0.14, 1.0);
gl.clearDepth(1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

Live demo:

Clear color and depth buffers

Demo is hidden by default. Click Show demo to render this recipe.

2. Compile shaders

ts
function compileShader(type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader) || 'shader compile failed');
  }

  return shader;
}

const vs = compileShader(
  gl.VERTEX_SHADER,
  `
  attribute vec2 aPos;
  void main() {
    gl_Position = vec4(aPos, 0.0, 1.0);
  }
`
);

const fs = compileShader(
  gl.FRAGMENT_SHADER,
  `
  precision mediump float;
  void main() {
    gl_FragColor = vec4(0.11, 0.78, 0.64, 1.0);
  }
`
);
ts
const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);

if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(program) || 'program link failed');
}

gl.useProgram(program);

4. Draw a triangle from a vertex buffer

ts
const vertices = new Float32Array([
  0.0, 0.7,
  -0.7, -0.6,
  0.7, -0.6,
]);

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

const aPos = gl.getAttribLocation(program, 'aPos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

gl.drawArrays(gl.TRIANGLES, 0, 3);

Live demo:

Draw a triangle from a vertex buffer

Demo is hidden by default. Click Show demo to render this recipe.

5. Animated uniform value (time)

ts
const animatedProgram = createProgram(
  gl,
  `
  attribute vec2 aPos;
  void main() {
    gl_Position = vec4(aPos, 0.0, 1.0);
  }
`,
  `
  precision mediump float;
  uniform float uTime;
  void main() {
    float v = 0.5 + 0.5 * sin(uTime * 2.0);
    gl_FragColor = vec4(v, 0.3, 1.0 - v, 1.0);
  }
`
);

// Reuse the buffer + attribute setup from recipe 4, then use animatedProgram.
gl.useProgram(animatedProgram);

let t = 0;
const uTime = gl.getUniformLocation(animatedProgram, 'uTime');

const timer = setInterval(() => {
  t += 0.016;
  gl.uniform1f(uTime, t);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
}, 16);

setTimeout(() => clearInterval(timer), 3000);

Live demo:

Animated uniform value

Demo is hidden by default. Click Show demo to render this recipe.

6. Alpha blending setup

ts
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

7. Depth testing for 3D scenes

ts
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);

8. Resize handling

ts
function resize() {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}

resize();

9. WebGL2 context fallback pattern

ts
const gl2 = canvas.getContext('webgl2') || canvas.getContext('webgl');
if (!gl2) {
  throw new Error('No GL context available');
}

10. Read pixels for debugging

ts
const pixels = new Uint8Array(4 * 16 * 16);
gl.readPixels(0, 0, 16, 16, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
console.log('sample pixel RGBA', pixels[0], pixels[1], pixels[2], pixels[3]);