Quick answer: In GameMaker, lower depth draws in front. If you assumed higher depth means drawn later, your layer order is inverted. Also, an instance assigned to a layer uses the layer’s depth — instance depth set in Create does nothing unless the instance is not on a layer.
Here is how to fix GameMaker layers that draw in an unexpected order even though the depth values look right. Your UI is behind the world. Your player is hidden by the background tilemap. You open the room editor, see layer depths exactly as you arranged them, and still the render looks scrambled. This is almost always one of three confusions about how GameMaker layer ordering actually works.
The Symptom
You have a room with a background layer, a world layer, and a UI layer. You set depths in the layer panel: background at 1000, world at 0, UI at -1000. You run the game and the background covers the world. Or you create a new layer dynamically for a particle effect and it renders behind everything instead of on top. Or an instance that sets depth = -100 in its Create event still draws behind a layer at depth 0.
Another common version: layers appear correct in the room editor preview but reorder themselves at runtime. Or they reorder when you go from the debug runner to the VM runner. Or a layer you created at runtime with layer_create(-5000) does not draw at all.
What Causes This
GameMaker Studio 2’s depth convention is counter-intuitive to many developers. Higher depth values are further back. Lower values are closer to the camera. The camera effectively looks down the negative depth axis, so a depth of -1000 is in front of 0, which is in front of 1000. This is the opposite of what you get in 3D engines where higher Z is typically further from the camera.
The common misreading: developers assume “higher depth means I want it drawn later, so higher depth means on top.” In GameMaker the opposite is true. A background at depth 1000 is further back. A UI at depth -1000 is in front.
The second cause: instance depth is ignored when the instance is on a layer. The room editor assigns every instance to some layer by default (Instances layer, or similar). That layer has a depth. The instance’s depth variable is still accessible but the rendering pipeline uses the layer’s depth for sort order. Changing depth in a Create event does nothing until you either remove the instance from its layer with instance_activate_layer(-1) (no longer the right approach in modern GameMaker) or move it to a different layer.
The third cause: dynamically created layers start at an unspecified depth. If you call layer_create(0) you get a layer at depth 0. But if another layer in the room already occupies depth 0, you get two layers at the same depth and the ordering between them is not guaranteed — sometimes creation order wins, sometimes the other layer renders first. Always use a unique depth, and usually leave gaps of at least 100 between layers so you have room to insert.
The Fix
Step 1: Audit the depth convention. Open your room. Pick three layers. Write down their depths and what you expect to see in front. Then run the game and observe. If your UI is drawing behind the world, invert the signs. A useful mnemonic: depth is distance from camera. The UI is closest, so the UI depth is smallest (or most negative).
Step 2: Use layer depth, not instance depth. If you have an instance that needs to draw in front, put it on a foreground layer. Do not set depth in Create and expect it to override the layer:
// Create event — WRONG if instance is on a layer
depth = -1000; // ignored, layer depth wins
// Correct: move the instance to a foreground layer
var target_layer = layer_get_id("UIForeground");
layer_add_instance(target_layer, id);
If you want an instance to use its own depth and not inherit a layer, create it with instance_create_depth instead of instance_create_layer:
// instance_create_depth creates an unmanaged-layer instance
var inst = instance_create_depth(x, y, -500, obj_Bullet);
Now inst.depth is authoritative because the instance is not bound to a layer.
Step 3: Set depth on dynamically created layers. Always specify a unique depth:
// Dynamically add an effect layer above the world but below UI
var world_layer = layer_get_id("World");
var ui_layer = layer_get_id("UI");
var world_depth = layer_get_depth(world_layer); // 0
var ui_depth = layer_get_depth(ui_layer); // -1000
// Pick a depth strictly between them
var effect_layer = layer_create(-500);
layer_set_target_room(room_get_current());
Step 4: Leave gaps between layer depths. Use spacing like 1000, 500, 0, -500, -1000 rather than 3, 2, 1, 0, -1. When you need to insert a layer later you have room to place it without renumbering every sibling. A common convention is to multiply every layer depth by 100 so you have 99 free slots between each.
If two layers share the same depth, GameMaker falls back to an arbitrary internal order based on creation time. Duplicate depths are the single most common cause of “flickering” draw order between runs.
Why This Works
GameMaker uses a single integer per layer to establish the painter’s-algorithm sort order. The renderer sorts layers from largest depth to smallest and draws in that sequence, so smaller depths paint last (on top). Once you internalize that convention, every “I thought higher depth meant in front” bug disappears.
Instance depth is retained from legacy GameMaker 8.x behavior where every instance had an implicit depth that controlled draw order. With the addition of layers, depth became a layer attribute first, and instance depth only matters when instances live outside managed layers. Using instance_create_depth explicitly opts into the legacy behavior.
Related Issues
If surfaces draw at incorrect depths or lose their content after a window resize, see Fix: GameMaker Surface Loss on Window Resize.
For tilemaps that render above instances you expect to be in front, see Fix: GameMaker Tilemap Draws Over Player — usually the tilemap layer depth is lower than the instance layer.
Lower depth is in front. Layer depth beats instance depth. Leave gaps between layers. Three rules, zero draw-order bugs.