Real-time graphics and simulation loops used to require native desktop builds. While JavaScript is incredibly versatile, it struggles with heavy CPU arithmetic at 60 frames per second. But modern browser APIs like WebAssembly (WASM), WebGL, and WebGPU change the equation. This article shows how to combine Rust, WASM, and hardware shaders to build web-based graphics engines that perform like native desktop applications.
WebAssembly is a binary instruction format designed as a portable compilation target for programming languages. When compiling Rust to WASM, the compiler generates optimized binary instructions that the browser's JavaScript engine can execute at near-native speed.
In a real-time audio visualizer like Shoggothox, CPU performance is critical. Every single frame (approx. 16.6 milliseconds), the engine must perform several heavy computations:
By writing these routines in Rust and compiling to WASM, we gain access to LLVM's advanced compiler optimizations, static typing, and memory safety, ensuring stable 60 FPS execution without memory leaks.
Once WASM performs the simulation, we must render the pixel grid to the
screen. A naive approach is drawing pixel-by-pixel using a 2D canvas
context (CanvasRenderingContext2D), but copying a large array
from WASM memory to a JS canvas context is expensive.
A much faster approach is using WebGL as a hardware-accelerated 2D blitter. Instead of drawing pixels on the CPU, we upload the raw
simulation buffer from WASM memory to the GPU as a 2D texture. In Shoggothox, the simulation buffer consists of single-byte
color indices (values from 0 to 255). We upload this as a 2D LUMINANCE texture
(a single-channel, grayscale format storing only brightness values rather
than full RGB components).
We also upload the active color palette as a second texture; the JavaScript-facing palette is 256 RGB colors, and the WebGL renderer expands it to 256 RGBA entries. In the WebGL fragment shader, we sample the simulation grid (our luminance texture) to get the index, and then use that index to look up the final RGB color in the palette texture:
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_buf; // 2D simulation index buffer
uniform sampler2D u_pal; // 1D palette lookup table
void main() {
float idx = texture2D(u_buf, v_uv).r;
gl_FragColor = texture2D(u_pal, vec2(idx, 0.5));
}
By delegating the palette lookup and texture interpolation (bilinear interpolation) to the GPU, we minimize CPU usage, leaving it free to focus entirely on the audio FFT and simulation loop.
While WebGL is excellent for 2D lookups, WebGPU is the next-generation standard for rendering and compute on the web. WebGPU provides modern graphics features similar to Vulkan, Metal, and DirectX 12. It reduces CPU driver overhead, enables compute shaders, and supports modern multithreaded rendering concepts.
In Shoggothox, WebGPU enables the 3D display modes and camera rotation in the browser build. ShoggothOx renders the active visualization buffer onto GPU-driven geometry such as planes, cylinders, spheres, height fields, and dual-plane views, using the graphics card to draw the result at interactive frame rates.
Combining Rust, WebAssembly, and modern GPU web APIs unlocks the full potential of browser-based rendering. Developers no longer need to compromise on performance when deploying tools to the web. By structuring your application to handle heavy simulation logic in Rust/WASM, and delegating pixel mapping and rasterization to WebGL/WebGPU shaders, you can build immersive, native-grade graphical experiences that load instantly in any browser, on any device.