Assembly is more fun when it draws something.
ASM Shader Toy is a little Shadertoy-style playground where the shader is written in a tiny fake assembly language.
The story is simple: a friend was learning assembly and the beginner exercises were boring. Move this value here. Print a number. Watch a register change. That is all useful, but it does not have much of a feedback loop.
I wanted the feedback loop to be immediate. Change an instruction, see pixels move. Branch wrong, the screen does something weird. Fix a constant, the planet rotates differently. It turns assembly from homework into a small instrument.
What it is
The language is not real x86, MIPS, RISC-V, or GPU assembly. It is a tiny teaching machine with registers,
labels, branches, calls, includes, constants, texture inputs, and bounded execution. Every pixel runs the
program with inputs like px, py, time, mouse_x, and
mouse_y.
.include <std/screen.inc>
mul tmp0, uv_x, tau
add color_r, tmp0, time
sin color_r, color_r
mul color_r, color_r, 0.5
add color_r, color_r, 0.5
out color_r, uv_y, 0.35, 1.0
That is enough to make plasma, mouse ripples, image effects, webcam effects, simple raymarching, and feedback buffer sketches like Conway's Game of Life.
How it grew
The first version was intentionally plain: a C++ assembler, a tiny VM, and a CPU renderer that ran the program once per pixel into a small intermediate texture. GBA resolution was a good default because it was small enough to be fast and large enough to feel like a real canvas.
Then the toy needed the stuff shader toys usually have: time, frame count, mouse, keyboard, images, video, webcam, audio, microphone, and feedback buffers. Feedback buffers are where it starts feeling less like a demo and more like a weird little graphics machine.
The CPU VM is still useful because it is easy to reason about and test. But the browser version needed the GPU. It now compiles the assembly to WGSL for WebGPU, and also to GLSL ES 3.00 for a WebGL2 fallback. That is why it can run in Firefox too.
The annoying bug
One of the fun bugs was the WebGL fallback. The generated shader used a big switch as the VM
dispatch loop. In WGSL, switch cases do not fall through. In GLSL, they do unless you add break.
The simple examples looked fine, but branch-heavy examples like the planet and Game of Life went completely
sideways.
There was also a coordinate mismatch because WebGL render targets are bottom-left oriented and the toy's pixel model is top-left oriented. So the fallback now renders in WebGL's native layout and flips only when presenting to the canvas.
What it is good for
- Learning why registers and branches matter without staring at a terminal.
- Seeing how simple math becomes an image.
- Messing with old-console resolutions and chunky pixels.
- Writing small shader sketches that feel more mechanical than GLSL.
It is a toy, but that is the point. The goal is not to replace real assembly or real shader languages. The goal is to make the first few hours of low-level thinking feel alive.