Webgl

by martinholovsky1086ef25672aNo license47 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 10 months ago

WebGL shaders and effects for JARVIS 3D HUD

AI-generated overview

Guides WebGL 2.0 shader and effect development for a 3D HUD, covering safe compilation, resource limits and performance patterns.

What it does
This skill supplies WebGL expertise for building custom shaders and visual effects in a JARVIS-style 3D HUD, including holographic shaders, post-processing and particle systems. It provides TypeScript and GLSL patterns for safe shader compilation, program linking, context-loss handling, uniform validation and resource management. It also documents performance practices such as buffer reuse, draw-call batching, texture compression, instancing and VAO usage, plus GPU resource limits and security notes. It is instructions only and produces no files or scripts.
When to use it
Use it when implementing or reviewing GPU-accelerated rendering code, such as custom shaders, holographic or post-processing effects, or real-time data visualization in a browser. It also fits when hardening WebGL code against context loss, GPU memory exhaustion or excessive draw calls.
Requirements
Requires a WebGL 2.0-capable browser and a TypeScript/JavaScript project; examples reference Vitest for tests and npm scripts for test, typecheck and build. No scripts or assets ship with the skill beyond two reference documents.

WebGL Development Skill

File Organization: This skill uses split structure. See references/ for advanced patterns and security examples.

1. Overview

This skill provides WebGL expertise for creating custom shaders and visual effects in the JARVIS AI Assistant HUD. It focuses on GPU-accelerated rendering with security considerations.

Risk Level: MEDIUM - Direct GPU access, potential for resource exhaustion, driver vulnerabilities

Primary Use Cases:

  • Custom shaders for holographic effects
  • Post-processing effects (bloom, glitch)
  • Particle systems with compute shaders
  • Real-time data visualization

2. Core Responsibilities

2.1 Fundamental Principles

  1. TDD First: Write tests before implementation - test shaders, contexts, and resources
  2. Performance Aware: Optimize GPU usage - batch draws, reuse buffers, compress textures
  3. GPU Safety: Implement timeout mechanisms and resource limits
  4. Shader Validation: Validate all shader inputs before compilation
  5. Context Management: Handle context loss gracefully
  6. Performance Budgets: Set strict limits on draw calls and triangles
  7. Fallback Strategy: Provide non-WebGL fallbacks
  8. Memory Management: Track and limit texture/buffer usage

3. Technology Stack & Versions

3.1 Browser Support

BrowserWebGL 2.0Notes
Chrome56+Full support
Firefox51+Full support
Safari15+WebGL 2.0 support
Edge79+Chromium-based

3.2 Security Considerations

typescript
// Check WebGL support and capabilitiesfunction getWebGLContext(canvas: HTMLCanvasElement): WebGL2RenderingContext | null {  const gl = canvas.getContext('webgl2', {    alpha: true,    antialias: true,    powerPreference: 'high-performance',    failIfMajorPerformanceCaveat: true  // Fail if software rendering  })
  if (!gl) {    console.warn('WebGL 2.0 not supported')    return null  }
  return gl}

4. Implementation Patterns

4.1 Safe Shader Compilation

typescript
// utils/shaderUtils.ts
// ✅ Safe shader compilation with error handlingexport function compileShader(  gl: WebGL2RenderingContext,  source: string,  type: number): WebGLShader | null {  const shader = gl.createShader(type)  if (!shader) return null
  gl.shaderSource(shader, source)  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {    const error = gl.getShaderInfoLog(shader)    console.error('Shader compilation error:', error)    gl.deleteShader(shader)    return null  }
  return shader}
// ✅ Safe program linkingexport function createProgram(  gl: WebGL2RenderingContext,  vertexShader: WebGLShader,  fragmentShader: WebGLShader): WebGLProgram | null {  const program = gl.createProgram()  if (!program) return null
  gl.attachShader(program, vertexShader)  gl.attachShader(program, fragmentShader)  gl.linkProgram(program)
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {    const error = gl.getProgramInfoLog(program)    console.error('Program linking error:', error)    gl.deleteProgram(program)    return null  }
  return program}

4.2 Context Loss Handling

typescript
// composables/useWebGL.tsexport function useWebGL(canvas: Ref<HTMLCanvasElement | null>) {  const gl = ref<WebGL2RenderingContext | null>(null)  const contextLost = ref(false)
  onMounted(() => {    if (!canvas.value) return
    // ✅ Handle context loss    canvas.value.addEventListener('webglcontextlost', (e) => {      e.preventDefault()      contextLost.value = true      console.warn('WebGL context lost')    })
    canvas.value.addEventListener('webglcontextrestored', () => {      contextLost.value = false      initializeGL()      console.info('WebGL context restored')    })
    initializeGL()  })
  function initializeGL() {    gl.value = getWebGLContext(canvas.value!)    // Reinitialize all resources  }
  return { gl, contextLost }}

4.3 Holographic Shader

glsl
// shaders/holographic.frag#version 300 esprecision highp float;
uniform float uTime;uniform vec3 uColor;uniform float uScanlineIntensity;
in vec2 vUv;out vec4 fragColor;
void main() {  // Scanline effect  float scanline = sin(vUv.y * 200.0 + uTime * 2.0) * 0.5 + 0.5;  scanline = mix(1.0, scanline, uScanlineIntensity);
  // Edge glow  float edge = smoothstep(0.0, 0.1, vUv.x) *               smoothstep(1.0, 0.9, vUv.x) *               smoothstep(0.0, 0.1, vUv.y) *               smoothstep(1.0, 0.9, vUv.y);
  vec3 color = uColor * scanline * edge;  float alpha = edge * 0.8;
  fragColor = vec4(color, alpha);}

4.4 Resource Management

typescript
// utils/resourceManager.tsexport class WebGLResourceManager {  private textures: Set<WebGLTexture> = new Set()  private buffers: Set<WebGLBuffer> = new Set()  private programs: Set<WebGLProgram> = new Set()
  private textureMemory = 0  private readonly MAX_TEXTURE_MEMORY = 256 * 1024 * 1024  // 256MB
  constructor(private gl: WebGL2RenderingContext) {}
  createTexture(width: number, height: number): WebGLTexture | null {    const size = width * height * 4  // RGBA
    // ✅ Enforce memory limits    if (this.textureMemory + size > this.MAX_TEXTURE_MEMORY) {      console.error('Texture memory limit exceeded')      return null    }
    const texture = this.gl.createTexture()    if (texture) {      this.textures.add(texture)      this.textureMemory += size    }    return texture  }
  dispose(): void {    this.textures.forEach(t => this.gl.deleteTexture(t))    this.buffers.forEach(b => this.gl.deleteBuffer(b))    this.programs.forEach(p => this.gl.deleteProgram(p))    this.textureMemory = 0  }}

4.5 Uniform Validation

typescript
// ✅ Type-safe uniform settingexport function setUniforms(  gl: WebGL2RenderingContext,  program: WebGLProgram,  uniforms: Record<string, number | number[] | Float32Array>): void {  for (const [name, value] of Object.entries(uniforms)) {    const location = gl.getUniformLocation(program, name)    if (!location) {      console.warn(`Uniform '${name}' not found`)      continue    }
    if (typeof value === 'number') {      gl.uniform1f(location, value)    } else if (Array.isArray(value)) {      switch (value.length) {        case 2: gl.uniform2fv(location, value); break        case 3: gl.uniform3fv(location, value); break        case 4: gl.uniform4fv(location, value); break        case 16: gl.uniformMatrix4fv(location, false, value); break      }    }  }}

5. Implementation Workflow (TDD)

5.1 Step-by-Step Process

  1. Write failing test -> 2. Implement minimum -> 3. Refactor -> 4. Verify
typescript
// Step 1: tests/webgl/shaderCompilation.test.tsimport { describe, it, expect, beforeEach } from 'vitest'import { compileShader } from '@/utils/shaderUtils'
describe('WebGL Shader Compilation', () => {  let gl: WebGL2RenderingContext
  beforeEach(() => {    gl = document.createElement('canvas').getContext('webgl2')!  })
  it('should compile valid shader', () => {    const source = `#version 300 es      in vec4 aPosition;      void main() { gl_Position = aPosition; }`    expect(compileShader(gl, source, gl.VERTEX_SHADER)).not.toBeNull()  })
  it('should return null for invalid shader', () => {    expect(compileShader(gl, 'invalid', gl.FRAGMENT_SHADER)).toBeNull()  })})
// Step 2-3: Implement and refactor (see section 4.1)// Step 4: npm test && npm run typecheck && npm run build

5.2 Testing Context and Resources

typescript
describe('WebGL Context', () => {  it('should handle context loss', async () => {    const { gl, contextLost } = useWebGL(ref(canvas))    gl.value?.getExtension('WEBGL_lose_context')?.loseContext()    await nextTick()    expect(contextLost.value).toBe(true)  })})
describe('Resource Manager', () => {  it('should enforce memory limits', () => {    const manager = new WebGLResourceManager(gl)    expect(manager.createTexture(1024, 1024)).not.toBeNull()    expect(manager.createTexture(16384, 16384)).toBeNull() // Exceeds limit  })})

6. Performance Patterns

6.1 Buffer Reuse

typescript
// Bad - Creates new buffer every frameconst buffer = gl.createBuffer()gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW)gl.deleteBuffer(buffer)
// Good - Reuse buffer, update only datagl.bufferSubData(gl.ARRAY_BUFFER, 0, data)  // Update existing buffer

6.2 Draw Call Batching

typescript
// Bad - One draw call per objectobjects.forEach(obj => {  gl.useProgram(obj.program)  gl.drawElements(...)})
// Good - Batch by material/shaderconst batches = groupByMaterial(objects)batches.forEach(batch => {  gl.useProgram(batch.program)  batch.objects.forEach(obj => gl.drawElements(...))})

6.3 Texture Compression

typescript
// Bad - Always uncompressed RGBAgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)
// Good - Use compressed formats when availableconst ext = gl.getExtension('WEBGL_compressed_texture_s3tc')if (ext) gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RGBA_S3TC_DXT5_EXT, ...)

6.4 Instanced Rendering

typescript
// Bad - Individual draw calls for particlesparticles.forEach(p => {  gl.uniform3fv(uPosition, p.position)  gl.drawArrays(gl.TRIANGLES, 0, 6)})
// Good - Single instanced draw callgl.drawArraysInstanced(gl.TRIANGLES, 0, 6, particles.length)

6.5 VAO Usage

typescript
// Bad - Rebind attributes every framegl.enableVertexAttribArray(0)gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0)
// Good - Use VAO to store attribute stateconst vao = gl.createVertexArray()gl.bindVertexArray(vao)// Set up once, then just bind VAO for rendering

7. Security Standards

7.1 Known Vulnerabilities

CVESeverityDescriptionMitigation
CVE-2024-11691HIGHApple M series memory corruptionUpdate browser, OS patches
CVE-2023-1531HIGHChrome use-after-freeUpdate Chrome

7.2 OWASP Top 10 Coverage

OWASP CategoryRiskMitigation
A06 Vulnerable ComponentsHIGHKeep browsers updated
A10 SSRFLOWContext isolation by browser

7.3 GPU Resource Protection

typescript
// ✅ Implement resource limitsconst LIMITS = {  maxDrawCalls: 100,  maxTriangles: 1_000_000,  maxTextures: 32,  maxTextureSize: 4096}
function checkLimits(stats: RenderStats): boolean {  if (stats.drawCalls > LIMITS.maxDrawCalls) {    console.error('Draw call limit exceeded')    return false  }  if (stats.triangles > LIMITS.maxTriangles) {    console.error('Triangle limit exceeded')    return false  }  return true}

8. Common Mistakes & Anti-Patterns

8.1 Critical Security Anti-Patterns

Never: Skip Context Loss Handling
typescript
// ❌ DANGEROUS - App crashes on context lossconst gl = canvas.getContext('webgl2')// No context loss handler!
// ✅ SECURE - Handle gracefullycanvas.addEventListener('webglcontextlost', handleLoss)canvas.addEventListener('webglcontextrestored', handleRestore)
Never: Unlimited Resource Allocation
typescript
// ❌ DANGEROUS - GPU memory exhaustionfor (let i = 0; i < userCount; i++) {  textures.push(gl.createTexture())}
// ✅ SECURE - Enforce limitsif (textureCount < MAX_TEXTURES) {  textures.push(gl.createTexture())}

8.2 Performance Anti-Patterns

Avoid: Excessive State Changes
typescript
// ❌ BAD - Unbatched draw callsobjects.forEach(obj => {  gl.useProgram(obj.program)  gl.bindTexture(gl.TEXTURE_2D, obj.texture)  gl.drawElements(...)})
// ✅ GOOD - Batch by materialbatches.forEach(batch => {  gl.useProgram(batch.program)  gl.bindTexture(gl.TEXTURE_2D, batch.texture)  batch.objects.forEach(obj => gl.drawElements(...))})

9. Pre-Implementation Checklist

Phase 1: Before Writing Code

  • Write failing tests for shaders, context, and resources
  • Define performance budgets (draw calls <100, memory <256MB)
  • Identify required WebGL extensions

Phase 2: During Implementation

  • Context loss handling with recovery
  • Resource limits and memory tracking
  • Shader validation before compilation
  • Use VAOs, batch draws, reuse buffers
  • Instanced rendering for particles

Phase 3: Before Committing

  • Tests pass: npm test -- --run tests/webgl/
  • Type check: npm run typecheck
  • Build: npm run build
  • Performance verified (draws, memory)
  • Fallback for no WebGL tested

10. Summary

WebGL provides GPU-accelerated graphics for JARVIS HUD. Key principles: handle context loss, enforce resource limits, validate shaders, track memory, batch draw calls, minimize state changes.

Remember: WebGL bypasses browser sandboxing - always protect against resource exhaustion. References: references/advanced-patterns.md, references/security-examples.md

Source and attribution

Source:martinholovsky/claude-skills-generatorinskills/webglat commit1086ef2

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal