The Cost of Abstracting the GPU
For the last five years, our studio has been obsessed with the bridge between raw mathematical expression and browser-based rendering. We’ve fought through memory leaks in Three.js, wrestled with unstable frame rates on mid-tier Android devices, and spent entire weekends debugging floating-point precision errors that only appeared on a specific version of Safari. The consensus among the team? Most of the architectural 'best practices' sold in documentation are actually death traps for performance.
When you start writing WebGL, you are told to modularize. Break your code down. Build a clean, object-oriented hierarchy. Ignore this advice. If you pass 50 individual objects through the main render loop, you aren't writing a creative application; you are writing a garbage collection nightmare. We shifted our approach to a data-oriented design—think flat arrays, typed buffers, and minimal state changes—and watched our draw calls plummet. Efficiency isn't about how pretty your class structure looks in VS Code; it’s about how few times you poke the GPU state.
The Shader Compiler is Your Toughest Critic
There is a specific kind of arrogance in thinking that a 500-line fragment shader is 'fine' because it compiles without errors. We learned the hard way that the GPU doesn't care about your readability. Last year, we were tasked with a complex fluid simulation. Our initial shader utilized nested loops and high-level conditional logic. It looked clean. It was readable. It also choked the frame rate to a staggering 14 FPS.
The GPU is a throughput beast. If you give it a branch to evaluate, you’ve already lost the game.
We had to strip the logic down to its raw components. We replaced dynamic branching with step functions and pre-calculated lookup tables. The code became uglier, certainly less 'professional' by standard software engineering metrics, but the output was butter-smooth. The ergonomics of creative code don't lie in the comfort of the developer; they lie in the comfort of the silicon.
The Tooling Debt
We’ve cycled through frameworks like we cycle through morning coffee. But the lesson remains constant: the more abstraction you put between yourself and the GLSL, the more you suffer when the edge cases hit. We moved toward a ‘bare-metal’ approach for our core engine components. We use raw WebGL2 contexts and manage our own vertex buffers. Is it more verbose? Yes. Does it save us from chasing phantom bugs in a black-box middleware? Every single time.
// The old way: heavy abstraction
const mesh = new SuperHighLevelComplexObject();
mesh.render(scene);
// The ergonomic way: flat and performant
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
// Minimal overhead, maximum control.Architecture as a Constraint
In web architecture, we often treat constraints as obstacles. In WebGL, constraints are the creative brief. The 16.6ms frame budget isn't a limit—it’s a discipline. When you stop fighting the browser and start working within the limits of the hardware, your code becomes tighter. You stop using 2K textures where 512px will do. You stop calculating every pixel when you can use a clever dithering pattern.
We stopped trying to make 'the perfect render engine.' Instead, we build engines for specific, narrow tasks. One project might need heavy compute shaders, another might need extreme geometry instancing. By building bespoke, throwaway architectures that serve only the project at hand, we avoid the bloated legacy of a 'master framework' that tries to do everything and ends up doing nothing particularly well.
Your code is a physical object. It has weight, it has friction, and it has a temperature. If your project is heating up a user's laptop fan, you have failed the ergonomics test. Stop worrying about how your code looks in a PR and start worrying about how it occupies the user's hardware. The most elegant code is the code that vanishes, leaving only the experience behind.
READER ESSAY DISCUSSIONS (0)
Share your architectural perspectives, technical notes, or critique.
No discussions recorded yet.
Be the first to spark the dialogue on this essay.