Tips & Tricks

Canvas/WebGL Optimization with Claude Code

Learn about Canvas/WebGL optimization using Claude Code. Practical tips and code examples included.

The Appeal and Challenges of Canvas/WebGL Development

Canvas is useful for a wide range of use cases including games, data visualization, image editing, and interactive art. On the flip side, the API is low-level and code tends to get verbose. With Claude Code, you can quickly generate rendering logic and shader code to dramatically speed up development.

A Basic Canvas 2D Rendering System

> Create a class that manages Canvas 2D rendering.
> Include an animation loop, resize handling, and FPS display.
class CanvasApp {
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;
  private animationId: number = 0;
  private lastTime: number = 0;
  private fps: number = 0;
  private frameCount: number = 0;
  private fpsTime: number = 0;

  constructor(container: HTMLElement) {
    this.canvas = document.createElement('canvas');
    this.ctx = this.canvas.getContext('2d')!;
    container.appendChild(this.canvas);
    this.resize();
    window.addEventListener('resize', () => this.resize());
  }

  private resize() {
    const dpr = window.devicePixelRatio || 1;
    const rect = this.canvas.parentElement!.getBoundingClientRect();
    this.canvas.width = rect.width * dpr;
    this.canvas.height = rect.height * dpr;
    this.canvas.style.width = `${rect.width}px`;
    this.canvas.style.height = `${rect.height}px`;
    this.ctx.scale(dpr, dpr);
  }

  start(renderFn: (ctx: CanvasRenderingContext2D, dt: number) => void) {
    const loop = (time: number) => {
      const dt = (time - this.lastTime) / 1000;
      this.lastTime = time;

      // Calculate FPS
      this.frameCount++;
      if (time - this.fpsTime >= 1000) {
        this.fps = this.frameCount;
        this.frameCount = 0;
        this.fpsTime = time;
      }

      const { width, height } = this.canvas.getBoundingClientRect();
      this.ctx.clearRect(0, 0, width, height);

      renderFn(this.ctx, dt);

      // Display FPS
      this.ctx.fillStyle = '#0f0';
      this.ctx.font = '12px monospace';
      this.ctx.fillText(`FPS: ${this.fps}`, 10, 20);

      this.animationId = requestAnimationFrame(loop);
    };

    this.animationId = requestAnimationFrame(loop);
  }

  stop() {
    cancelAnimationFrame(this.animationId);
  }
}

A Particle System

> Implement a particle effect. Create particles that follow the mouse.
interface Particle {
  x: number; y: number;
  vx: number; vy: number;
  life: number; maxLife: number;
  size: number;
  color: string;
}

class ParticleSystem {
  private particles: Particle[] = [];
  private maxParticles = 500;

  emit(x: number, y: number, count: number = 5) {
    for (let i = 0; i < count; i++) {
      if (this.particles.length >= this.maxParticles) break;

      const angle = Math.random() * Math.PI * 2;
      const speed = Math.random() * 3 + 1;
      const hue = Math.random() * 60 + 200; // blue to purple

      this.particles.push({
        x, y,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed,
        life: 1,
        maxLife: Math.random() * 1 + 0.5,
        size: Math.random() * 4 + 2,
        color: `hsl(${hue}, 80%, 60%)`,
      });
    }
  }

  update(dt: number) {
    this.particles = this.particles.filter((p) => {
      p.x += p.vx;
      p.y += p.vy;
      p.vy += 0.5 * dt; // gravity
      p.life -= dt / p.maxLife;
      return p.life > 0;
    });
  }

  draw(ctx: CanvasRenderingContext2D) {
    this.particles.forEach((p) => {
      ctx.globalAlpha = p.life;
      ctx.fillStyle = p.color;
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2);
      ctx.fill();
    });
    ctx.globalAlpha = 1;
  }
}

WebGL Shaders

> Create a simple gradient shader in WebGL.
function createShaderProgram(gl: WebGLRenderingContext) {
  const vertexShaderSource = `
    attribute vec2 a_position;
    varying vec2 v_uv;

    void main() {
      v_uv = a_position * 0.5 + 0.5;
      gl_Position = vec4(a_position, 0.0, 1.0);
    }
  `;

  const fragmentShaderSource = `
    precision mediump float;
    varying vec2 v_uv;
    uniform float u_time;

    void main() {
      vec3 color1 = vec3(0.1, 0.3, 0.8);
      vec3 color2 = vec3(0.8, 0.2, 0.5);
      float t = sin(v_uv.x * 3.0 + u_time) * 0.5 + 0.5;
      vec3 color = mix(color1, color2, t * v_uv.y);
      gl_FragColor = vec4(color, 1.0);
    }
  `;

  const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
  const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

  const program = gl.createProgram()!;
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);

  return program;
}

function compileShader(gl: WebGLRenderingContext, type: number, source: string) {
  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 error');
  }
  return shader;
}

React Integration

function CanvasComponent() {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const app = new CanvasApp(containerRef.current);
    const particles = new ParticleSystem();

    containerRef.current.addEventListener('mousemove', (e) => {
      const rect = containerRef.current!.getBoundingClientRect();
      particles.emit(e.clientX - rect.left, e.clientY - rect.top);
    });

    app.start((ctx, dt) => {
      particles.update(dt);
      particles.draw(ctx);
    });

    return () => app.stop();
  }, []);

  return <div ref={containerRef} className="w-full h-96 bg-gray-900 rounded-lg" />;
}

Summary

With Claude Code, you can efficiently build Canvas 2D rendering systems, particle effects, and WebGL shaders. For audio integration, see the Web Audio API guide, and for visualization, see the data visualization article.

For details on the Canvas API, see MDN Web Docs - Canvas API, and for WebGL, see WebGL Fundamentals.

#Claude Code #Canvas #WebGL #graphics #TypeScript

Level up your Claude Code workflow

50 battle-tested prompt templates you can copy-paste into Claude Code right now.

Free

Free PDF: Claude Code Cheatsheet in 5 Minutes

Key commands, shortcuts, and prompt examples on a single printable page.

Download PDF
M

About the Author

Masa

Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.