The CPU Shader Revolution
Most creative coding happens on the GPU. Processing happens in parallel across thousands of cores, manipulating pixels at incredible speed. It's the obvious place for real-time graphics.
So why am I writing shaders that run on the CPU?
The Obvious Path
Every creative coding framework takes GPU shaders as a given:
- p5.js:
createShader()compiles GLSL - Processing: P2D/P3D use OpenGL
- Three.js: ShaderMaterial, GLSL, always GLSL
- GLSL this, fragment shader that
The assumption is universal: if you want to manipulate pixels, you need a graphics card. The CPU is for game logic, physics, AI — anything but the visual stuff.
This assumption is wrong.
The Case for CPU Shaders
Here's what I've learned building a pixel manipulation system in rust-sketch:
1. Simplicity wins
fn fragment(
&self,
x: i32,
y: i32,
width: u32,
height: u32,
framebuffer: &mut [u32],
time: f64,
mouse_x: f32,
mouse_y: f32,
) -> Option<Color>
This is it. One function. No compiling, no uniform binding, no shader programs to link. Just code that runs for every pixel.
2. Debugging is straightforward
You can println! inside a CPU shader. You can attach a debugger. You can print the color of any pixel and see exactly what's happening. Try doing that with a GPU shader.
3. Portability is free
GPU shaders require context creation, API negotiation (Vulkan vs Metal vs OpenGL vs DirectX), driver quirks, format conversions. CPU code just runs.
4. The performance trade-off is acceptable
For 800×600 resolution, that's 480,000 pixels. Modern CPUs handle millions of operations per frame. A gradient or blur effect? Instant. Even complex noise functions run at 60fps.
Where it hurts: large kernels (gaussian blur at radius 50), complex raymarching, anything requiring massive per-pixel computation. But for most creative coding — particles, noise, color manipulation — CPU is plenty fast.
The Real Reason
Here's what's actually going on: I'm not building a game engine. I'm building a sketching tool. The goal is rapid iteration, easy understanding, code you can read and modify.
GPU shaders are an abstraction layer. They add complexity to gain performance I don't always need.
The CPU approach is honest about its trade-offs. It says: "This is simple. This is portable. This is readable. It runs at 60fps for most things. Deal with it."
That's a better fit for creative coding as exploration rather than production rendering.
What This Means
The next time someone says "you need shaders for that," ask: do I?
Sometimes the answer is yes. But often the answer is: just write a loop over your pixels. Let the CPU do what CPUs do well.
The tool should fit the person, not the other way around.
Sung.