Develop high-performance WebGL shaders and effects with a security-first approach.

Install

mkdir -p .claude/skills/webgl && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/9611" && unzip -o skill.zip -d .claude/skills/webgl && rm skill.zip

Installs to .claude/skills/webgl

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

WebGL shaders and effects for JARVIS 3D HUD
43 charsno explicit “when” trigger
Advanced

Key capabilities

  • Create custom shaders
  • Implement post-processing
  • Manage GPU resources

How it works

It provides expertise for GPU-accelerated rendering and shader development with security considerations.

Inputs & outputs

You give it
shader source
You get back
rendered effect

When to use webgl

  • Create holographic shader
  • Optimize particle system
  • Add bloom post-processing

About this skill

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

// Check WebGL support and capabilities
function 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

// utils/shaderUtils.ts

// ✅ Safe shader compilation with error handling
export 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 linking
export 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

// composables/useWebGL.ts
export 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

// shaders/holographic.frag
#version 300 es
precision 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

// utils/resourceManager.ts
export 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

// ✅ Type-safe uniform setting
export 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
// Step 1: tests/webgl/shaderCompilation.test.ts
import { 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

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

// Bad - Creates new buffer every frame
const buffer = gl.createBuffer();
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
gl.deleteBuffer(buffer);

// Good - Reuse buffer, update only data
gl.bufferSubData(gl.ARRAY_BUFFER, 0, data); // Update existing buffer

6.2 Draw Call Batching

// Bad - One draw call per object
objects.forEach(obj => {
  gl.useProgram(obj.program)
  gl.drawElements(...)
})

// Good - Batch by material/shader
const batches = groupByMaterial(objects)
batches.forEach(batch => {
  gl.useProgram(batch.program)
  batch.objects.forEach(obj => gl.drawElements(...))
})

6.3 Texture Compression

// Bad - Always uncompressed RGBA
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)

// Good - Use compressed formats when available
const 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

// Bad - Individual draw calls for particles
particles.forEach((p) => {
  gl.uniform3fv(uPosition, p.position);
  gl.drawArrays(gl.TRIANGLES, 0, 6);
});

// Good - Single instanced draw call
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, particles.length);

6.5 VAO Usage

// Bad - Rebind attributes every frame
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);

// Good - Use VAO to store attribute state
const vao =

---

*Content truncated.*

When not to use it

  • High-level UI logic
  • Non-WebGL tasks

Prerequisites

WebGL 2.0

Limitations

  • Requires WebGL 2.0
  • Risk of resource exhaustion

How it compares

It focuses on GPU safety and performance budgets for holographic effects.

Compared to similar skills

webgl side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
webgl (this skill)06moNo flagsAdvanced
scroll-experience1016moNo flagsIntermediate
interaction-design155moNo flagsIntermediate
motion106moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

scroll-experience

davila7

Expert in building immersive scroll-driven experiences - parallax storytelling, scroll animations, interactive narratives, and cinematic web experiences. Like NY Times interactives, Apple product pages, and award-winning web experiences. Makes websites feel like experiences, not just pages. Use when: scroll animation, parallax, scroll storytelling, interactive story, cinematic website.

101142

interaction-design

wshobson

Design and implement microinteractions, motion design, transitions, and user feedback patterns. Use when adding polish to UI interactions, implementing loading states, or creating delightful user experiences.

1550

motion

onmax

Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked effects, layout transitions, and composables for Vue 3/Nuxt

1044

frontend-enhancer

ailabs-393

This skill should be used when enhancing the visual design and aesthetics of Next.js web applications. It provides modern UI components, design patterns, color palettes, animations, and layout templates. Use this skill for tasks like improving styling, creating responsive designs, implementing modern UI patterns, adding animations, selecting color schemes, or building aesthetically pleasing frontend interfaces.

35

threejs-postprocessing

CloudAI-X

Three.js post-processing - EffectComposer, bloom, DOF, screen effects. Use when adding visual effects, color grading, blur, glow, or creating custom screen-space shaders.

17

animation-performance-retro

TheOrcDev

Optimize 8-bit animations for smooth performance. Apply when creating animated pixel art, game UI effects, or any retro-styled animations.

10

Search skills

Search the agent skills registry