{"id":"shader--gpu-gradient","name":"Paint your first pixels","owner":"yeho","package":"examples.gpu-shaders","kind":"shader","signature":"[gpu] compute Paint(buffer of int pixels, int width, int height, float time)","description":"Give each GPU invocation one pixel. Turn its coordinates into color.","context":"A compute shader is a small program that runs across data on the GPU. Here the data is an image. Yeho translates the [gpu] compute body to the selected provider; on Mac, that provider is Metal.","parameters":[],"returns":"","members":[],"tags":["complete-program","compute","gpu","image","shader","yeho"],"updated":"2026-09-09","source":{"repository":"demonhunterlabs","path":"app/lib/yeho-gpu-shaders.ts","sha256":"b3983d5e900977c6c16677b723df3bb26d41ef4c77cccd618d044f05f27a36e7"},"status":"native-metal-example","notes":["Swap u and v in the red and green expressions. Then set b to 0.0. Rebuild and compare shader.ppm.","The dedicated shader verifier executes this program on Metal and compares every output channel with a separate CPU reference. Evidence: https://demonhunterlabs.com/learning/gpu-shaders/verification.json","The program writes shader.ppm in its current directory. Native image generation is separate from presenting frames in a game window."],"examples":[{"id":"complete","title":"01 / Paint your first pixels","description":"Give each GPU invocation one pixel. Turn its coordinates into color.","code":"[gpu]\ncompute Paint(buffer of int pixels, int width, int height, float time)\n{\n    int index = compute.index\n    if index >= pixels.count { return }\n    int row = index / width\n    int column = index - row * width\n    float u = Math.IntToFloat(column) / Math.IntToFloat(width - 1)\n    float v = Math.IntToFloat(row) / Math.IntToFloat(height - 1)\n    float r = 0.08 + u * 0.8\n    float g = 0.08 + v * 0.8\n    float b = 0.3 + (1.0 - u) * 0.6\n    int red = Math.RoundToInt(Math.ClampFloat(r, 0.0, 1.0) * 255.0)\n    int green = Math.RoundToInt(Math.ClampFloat(g, 0.0, 1.0) * 255.0)\n    int blue = Math.RoundToInt(Math.ClampFloat(b, 0.0, 1.0) * 255.0)\n    pixels[index] = red * 65536 + green * 256 + blue\n}\n\n// CPU: allocate an image and submit one GPU invocation per pixel.\nint width = 128\nint height = 128\nbuffer of int pixels\npixels.Resize(width * height)\ncomputeTask job = dispatch Paint(pixels, width, height, 0.0)\nwait job\nif job.failed { Console.Error(job.error.message) Process.Exit(1) }\n\n// CPU: save the completed RGB pixels as a portable PPM image.\ntext image = \"P3\\n\" + Text.From(width) + \" \" + Text.From(height) + \"\\n255\\n\"\nfor row from 0 < height\n{\n    text line = \"\"\n    for column from 0 < width\n    {\n        int pixel = pixels[row * width + column]\n        int red = pixel / 65536\n        int green = (pixel - red * 65536) / 256\n        int blue = pixel - red * 65536 - green * 256\n        line = line + Text.From(red) + \" \" + Text.From(green) + \" \" + Text.From(blue) + \" \"\n    }\n    image = image + line + \"\\n\"\n}\nFile.WriteText(\"shader.ppm\", image)\nConsole.Log(\"Wrote shader.ppm\")\n","manifest":"manifestVersion = \"2\"\npackage = \"learning.shaders.gpu_gradient\"\nversion = \"0.1.0\"\nlanguageEdition = \"yeho-core-2026.1\"\n\n[app]\nkind = \"headless\"\n","tags":["gpu","shader","image"],"run":"metal","expected":"Wrote shader.ppm","verification":"type checked","sha256":"6e121dd5fb7d4e991e8e85d03d23eac56b590fa12a2d825aea95925c37a46d73","checked":"2026-09-09","diagnostic":"","observedOutput":null,"checkedBackend":null}],"searchTerms":[],"url":"https://demonhunterlabs.com/academy/api?item=shader--gpu-gradient","json":"https://demonhunterlabs.com/reference/items/shader--gpu-gradient.json","markdown":"https://demonhunterlabs.com/reference/text/shader--gpu-gradient.md"}
