Quick answer: draw_text uses the font texture page as gm_BaseTexture and submits geometry through the same pipeline as sprites. If your shader has an attribute name typo (in_Color vs in_Colour), does not sample gm_BaseTexture, or fails to compile silently, text vertices render incorrectly while sprite draws still work because sprites use different defaults. Start from the passthrough.vsh template, sample gm_BaseTexture in the fragment, and verify with shader_is_compiled.

Here is how to fix GameMaker shaders that work on draw_sprite but do nothing on draw_text. You wrote a shader that adds a wavy distortion to UI elements. Sprites distort beautifully. Text drawn inside the same shader_set / shader_reset block renders flat and unmodified, or worse, completely invisible. The shader is bound, the draw call ran, the text appeared in the wrong state. Two cases produce this: the shader compiles but uses the wrong attribute names so text colour binding fails, or the fragment never samples gm_BaseTexture and so the font glyph alpha is never read.

The Symptom

A custom shader applied via shader_set processes draw_sprite calls correctly but appears to do nothing — or to break rendering entirely — when applied to draw_text calls inside the same block.

Text renders unmodified. The shader effect (colour shift, distortion, outline) is visible on sprites but text appears in its default colour and shape with no visible shader processing. Both draw calls are inside the shader_set block, so the shader is active during the text draw — the effect just does not apply.

Text renders solid black. Text appears as solid black rectangles where each glyph would be. The shader is sampling something but not multiplying by the glyph alpha, so every pixel inside the glyph quad is filled with the base colour rather than only the glyph silhouette.

Text renders fully transparent. The text is invisible. The shader is sampling but with wrong texture coordinates, or the colour attribute is reading uninitialised data and resolving to alpha zero.

Text renders in the wrong colour. draw_set_color calls before draw_text are ignored. Text appears in white or in a previous colour. The shader is not reading the per-vertex colour because in_Colour was misspelled as in_Color, so the colour stream never binds.

What Causes This

Attribute name typo. GameMaker passes vertex attributes with British spelling: in_Position, in_Colour, in_TextureCoord. The American spelling in_Color compiles without error in GLSL ES but does not bind to any GameMaker-provided stream. Sprite drawing happens to look correct because sprites are usually drawn at full white and the missing colour stream defaults to (0,0,0,0) on some platforms and (1,1,1,1) on others — sprites mask the bug. Text relies on the colour stream for tint and transparency, so it fails visibly.

Missing gm_BaseTexture sample. If your fragment shader outputs a constant colour without sampling gm_BaseTexture, sprites still render as solid blocks (which can look intentional). Text renders as solid blocks too, but text is supposed to read the glyph alpha from the font texture — without that read, every pixel inside the glyph quad is filled, producing the “text as black rectangles” symptom.

Wrong projection matrix. Custom vertex shaders sometimes hand-build the projection matrix instead of using gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]. If the matrix is correct only for sprite quads (which are drawn at large sizes) but wrong for the small triangles that make up text glyphs, the text renders at zero size or off-screen.

Font texture not on expected sampler. When draw_text runs, the font’s texture page is bound as gm_BaseTexture for that draw call. If your shader expects the texture on a custom sampler (set with shader_set_uniform_i), the font texture is never visible to your sampling code.

Shader silently failed to compile. If your shader fails to compile, GameMaker logs an error in the Output Log but shader_set succeeds anyway and silently falls back to the default passthrough shader. Sprites then appear unaffected (they would have rendered the same with passthrough) and text appears unaffected for the same reason — you never had a custom shader running.

The Fix

Step 1: Start from a passthrough template. Create a new shader and paste the contents of GameMaker’s default passthrough.vsh and passthrough.fsh as your baseline. Modify only what you need to change. This guarantees attribute spelling and matrix references are correct.

// shd_text_wave Create event — verify before use
if (!shader_is_compiled(shd_text_wave)) {
    show_debug_message("shd_text_wave failed to compile");
    show_message("Shader compile error — check Output Log");
    exit;
}

// Cache uniform locations once
u_time = shader_get_uniform(shd_text_wave, "u_time");
u_amplitude = shader_get_uniform(shd_text_wave, "u_amplitude");

show_debug_message("Text shader ready");

The shader_is_compiled check at startup is the single most useful diagnostic for shader bugs. If it returns false, no amount of shader_set will activate your code — you are running passthrough. Catch this once at boot rather than spending hours wondering why your effect does nothing.

Step 2: Wrap draw_text inside shader_set with correct uniforms. The shader is only active between shader_set and shader_reset. Set uniforms before the draw call, draw the text, then reset.

// obj_ui Draw GUI event
if (!shader_is_compiled(shd_text_wave)) {
    draw_text(10, 10, "Shader broken — falling back");
    exit;
}

shader_set(shd_text_wave);
shader_set_uniform_f(u_time, current_time / 1000);
shader_set_uniform_f(u_amplitude, 2.5);

draw_set_color(c_white);
draw_set_font(fnt_main);
draw_set_halign(fa_center);
draw_text(display_get_gui_width() / 2, 100, "WAVING HEADLINE");

shader_reset();
draw_set_halign(fa_left);

Set the colour and font before draw_text, not before shader_set. The shader picks up the per-vertex colour from v_vColour in the fragment, which is populated from in_Colour in the vertex shader, which is populated from the active draw_set_color at the time of the draw call.

Vertex Shader Template That Works for Text

The minimum vertex shader that handles both sprite and text drawing correctly:

// shd_text_wave .vsh source
attribute vec3 in_Position;
attribute vec4 in_Colour;
attribute vec2 in_TextureCoord;

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform float u_time;
uniform float u_amplitude;

void main() {
    vec4 pos = vec4(in_Position, 1.0);
    pos.y += sin(pos.x * 0.05 + u_time) * u_amplitude;

    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * pos;

    v_vColour = in_Colour;
    v_vTexcoord = in_TextureCoord;
}

Note three details: in_Colour with the British spelling, the use of gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] for the projection so the math matches whatever GameMaker is doing for that draw, and the wave being applied in object space before projection so it scales correctly with text size.

Fragment Shader That Renders Text Correctly

The fragment must sample gm_BaseTexture and multiply by v_vColour:

// shd_text_wave .fsh source
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main() {
    vec4 tex = texture2D(gm_BaseTexture, v_vTexcoord);
    gl_FragColor = v_vColour * tex;
}

The v_vColour * tex multiplication is what makes text visible. The font glyph alpha lives in tex.a; multiplying by the white-with-tint v_vColour produces a glyph-shaped pixel with the requested colour. Drop the multiplication and you get solid colour rectangles. Drop the texture sample and you get nothing at all.

“Sprites are forgiving because they hide your bugs behind their own opacity. Text exposes every shader mistake because text is mostly transparent — the bugs have nowhere to hide.”

Related Issues

If your shader works on text in fixed-pixel fonts but breaks on SDF fonts, see SDF Font Shader Setup for distance-field sampling. If your shader compiles in IDE but breaks on HTML5 export, check Shader Precision Differences on HTML5 for highp/mediump fixes.

Spell it in_Colour. The u catches more bugs than any debugger ever will.