Back to the toy

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

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.

Open ASM Shader Toy