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.zipInstalls 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 HUDKey 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
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
- TDD First: Write tests before implementation - test shaders, contexts, and resources
- Performance Aware: Optimize GPU usage - batch draws, reuse buffers, compress textures
- GPU Safety: Implement timeout mechanisms and resource limits
- Shader Validation: Validate all shader inputs before compilation
- Context Management: Handle context loss gracefully
- Performance Budgets: Set strict limits on draw calls and triangles
- Fallback Strategy: Provide non-WebGL fallbacks
- Memory Management: Track and limit texture/buffer usage
3. Technology Stack & Versions
3.1 Browser Support
| Browser | WebGL 2.0 | Notes |
|---|---|---|
| Chrome | 56+ | Full support |
| Firefox | 51+ | Full support |
| Safari | 15+ | WebGL 2.0 support |
| Edge | 79+ | 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
- 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
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.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| webgl (this skill) | 0 | 6mo | No flags | Advanced |
| scroll-experience | 101 | 6mo | No flags | Intermediate |
| interaction-design | 15 | 5mo | No flags | Intermediate |
| motion | 10 | 6mo | Review | Intermediate |
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.
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.
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
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.
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.
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.