How gpu.js Converts JavaScript to WebGL Shaders

This article explains the internal transpilation pipeline of gpu.js, detailing how standard JavaScript functions are converted into high-performance WebGL shader code. By parsing JavaScript into an Abstract Syntax Tree, inferring data types, mapping built-in functions, and injecting memory-management boilerplate, gpu.js bridges the gap between dynamic browser code and statically typed GLSL (OpenGL Shading Language) execution.

1. Function Stringification and AST Parsing

The process begins when a standard JavaScript function is passed into the gpu.createKernel() method. Because JavaScript engines execute functions natively rather than exposing raw GLSL, gpu.js must deconstruct the source code:

2. Static Type Inference

GLSL requires strict, static typing, whereas JavaScript is dynamically typed. To generate valid shader code, gpu.js performs a static analysis pass over the AST:

3. Syntax and Math Mapping

Once the AST nodes are typed, gpu.js traverses the tree and translates JavaScript syntax into corresponding GLSL language constructs:

4. Memory Layout and Texture Mapping

GPUs process large data parallelly by drawing to screen pixels or off-screen framebuffers. gpu.js handles data input and output via WebGL textures:

5. GLSL Assembly and WebGL Compilation

After transforming the core logic, gpu.js wraps the translated statements into a complete GLSL Fragment Shader template:

  1. Header Injection: Precision specifiers (e.g., precision highp float;) and uniform declarations for kernel arguments are injected at the top.
  2. Main Function Insertion: The transformed kernel body is placed inside void main() { ... }.
  3. Output Encoding: The kernel’s return value is packed into the gl_FragColor vector, encoding numerical results across the four color channels (RGBA) if floating-point textures are unsupported, or directly bound if supported.

Finally, this assembled GLSL string is passed to the browser's WebGL context via gl.shaderSource() and compiled with gl.compileShader(). The resulting WebGL program executes in parallel across the GPU cores, returning the computed results directly back to JavaScript.