GameMaker shaders (GLSL ES)

GameMaker uses GLSL ES, the mobile-friendly subset of GLSL. Shaders there are written in a .fsh file next to a vertex shader. Build the effect here, then export it for a Shader asset and set the uniforms from GML.

Things to know about GLSL

  • GameMaker uses GLSL ES 1.0: there is no round() or trunc(), and integer tricks are limited.
  • Texture coordinates start at the top-left; the export flips y.
  • Set uniforms with shader_set_uniform_f(shader_get_uniform(sh_mine, "time"), current_time / 1000).

Cheat sheet

IdeaWrite
Vectorsvec2 vec3 vec4
Declare a valuefloat d = length(p);
A functionfloat circle(vec2 p, float r) { ... }
Matricesmat2 mat3, multiply with M * v (this site writes v * M for lesson rotations)
Blendmix(a, b, t)
Fractional partfract(x)
Clamp to 0..1clamp(x, 0.0, 1.0)
Remaindermod(a, b) (always positive for positive b)
Choosec ? a : b
Angle of a vectoratan(y, x)
Time in secondstime
Pixel positionuv (0..1, bottom-left)
Width / heightaspect
Entry pointvec4 frag(vec2 uv) { return …; }

Handy built-ins: sin cos abs sign floor ceil fract step smoothstep clamp min max pow exp sqrt length distance dot cross normalize reflect

Gotchas

  • Every pixel runs on its own, with no memory of other pixels.
  • Declare a function above the place you use it.
  • Prefer 2.0 over 2 in maths. This site fixes most cases for you; real engines may not.
  • Loops need a small fixed count: for (int i = 0; i < 5; i++).
  • GameMaker uses GLSL ES 1.0: no round(), trunc() or integer bit tricks.

Example: a glowing ring

The same shader as it is written for GameMaker:

vec4 frag(vec2 uv)
{
    vec2 p = (uv - 0.5) * vec2(aspect, 1.0);
    float d = length(p);
    float m = 1.0 - smoothstep(0.0, 0.02, abs(d - 0.3));
    vec3 bg = vec3(0.08, 0.08, 0.12);
    vec3 fg = vec3(0.4, 0.9, 1.0);
    return vec4(mix(bg, fg, m), 1.0);
}

Exporting to GameMaker

Create a Shader asset (GLSL ES) and paste this into the .fsh file, keeping the default vertex shader. In the Draw event: shader_set(sh_mine); shader_set_uniform_f(shader_get_uniform(sh_mine, "time"), current_time / 1000); shader_set_uniform_f(shader_get_uniform(sh_mine, "aspect"), w / h); then draw a sprite or rectangle and call shader_reset(). GameMaker uses GLSL ES 1.0, so round() and trunc() are not available.

// ShaderBattle: Glowing ring
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform float time;
uniform float aspect;

vec4 frag(vec2 uv)
{
    vec2 p = (uv - 0.5) * vec2(aspect, 1.0);
    float d = length(p);
    float m = 1.0 - smoothstep(0.0, 0.02, abs(d - 0.3));
    vec3 bg = vec3(0.08, 0.08, 0.12);
    vec3 fg = vec3(0.4, 0.9, 1.0);
    return vec4(mix(bg, fg, m), 1.0);
}

void main()
{
    // GameMaker's texture coordinates start at the top-left
    gl_FragColor = frag(vec2(v_vTexcoord.x, 1.0 - v_vTexcoord.y));
}

Questions

Do I write the vertex shader too?

No. The default vertex shader works; the export only replaces the fragment shader.

Other languages

Unity (Built-in) · Unity (URP) · Unreal · Shadertoy / WebGL · OpenGL (LWJGL, raylib) · three.js · LÖVE (Lua) · Godot · Apple (iOS, macOS) · WebGPU / Bevy