Y-Depth Sorting
Sprite ordering by position
The technique of sorting sprites by their Y position to create correct depth ordering in top-down and isometric games, essential for beat 'em ups and action RPGs.
Overview
Y-depth sorting is the technique of ordering sprites by their vertical position so objects lower on screen appear in front of objects higher on screen. This creates correct visual layering in top-down and isometric games where "lower on screen = closer to camera." It's the simplest depth-sorting method that works — older than the formal painter's algorithm (1972) in arcade game form, made standard practice by Marble Madness, Knight Lore, and the early beat-em-up genre.
Fast facts
| Aspect | Detail |
|---|---|
| Also called | Painter's algorithm (2D), Y-sorting, depth-sorting |
| Problem solved | Correct sprite-overlap ordering when sprites can overlap |
| Key genres | Beat-em-ups, action RPGs, isometric, top-down adventure |
| Modern equivalent | Z-sorting, depth buffer, transparent-sort layer in modern engines |
The core principle
In top-down or 3/4 perspective, the screen-Y axis maps directly to camera-distance:
| Screen Y | Depth interpretation |
|---|---|
| Higher Y (top of screen) | Further from camera |
| Lower Y (bottom of screen) | Closer to camera |
Objects closer to the camera should draw last (so they overlap things further away). Sort sprites by Y ascending, then draw in that order. The object lowest on screen draws on top.
Sorting approaches
| Method | Time complexity | Best for |
|---|---|---|
| Bubble sort | O(n²) worst, O(n) best | Few sprites, nearly-sorted (last frame's order) |
| Insertion sort | O(n²) worst, O(n) best | Nearly-sorted — most frames don't reorder much |
| Bucket sort | O(n + k) | Fixed Y precision (e.g. one bucket per scanline) |
| Radix sort | O(n × digits) | Many sprites, fixed-width Y values |
Insertion sort wins in practice for game sprites — most frames have only a few objects swap order, so insertion sort runs near O(n).
Implementation considerations
| Factor | Impact |
|---|---|
| Sort key | Usually the sprite's foot Y position (where it touches the ground), not the sprite's centre or top |
| Tie-breaking | When two sprites share the same Y, use X position or object ID for stable ordering |
| Frequency | Every frame — positions change every frame |
| Stability | Stable sort prevents flicker when sprites have identical Y |
Platform-specific challenges
Hardware sprites (C64, NES)
Hardware-sprite systems have fixed sprite priority slots — sprite 0 always draws over sprite 1, etc. To Y-sort, the game must shuffle which graphics go in which sprite slot:
| Constraint | Solution |
|---|---|
| Fixed slot priority | Move sprite graphics between slots based on Y order |
| 8 sprites per scanline (NES) | Combine Y-sort with sprite multiplexing |
| Priority registers | Set background-priority bits based on sorted order |
Bitmap rendering (Amiga, PC, Spectrum)
Software sprite systems draw sprites in any order, but the order matters absolutely:
| Constraint | Solution |
|---|---|
| Draw order matters | Render back-to-front; back sprites drawn first, front sprites drawn last |
| Transparency | Each sprite must respect those drawn before |
| Overdraw cost | Far objects may be obscured — minimise overdraw via culling |
Beat-em-up example
In a side-scrolling beat-em-up like Final Fight or Streets of Rage:
| Object | Foot Y | Draw order |
|---|---|---|
| Background enemy | Y = 80 | Drawn first |
| Player | Y = 120 | Drawn second |
| Foreground enemy | Y = 160 | Drawn last (on top) |
When the player walks behind a barrel, it's because the barrel's Y is higher (further down the screen) than the player's. Walk in front: the player's Y is higher. Same code, different positions.
Optimisation techniques
| Technique | Benefit |
|---|---|
| Insertion sort with prior order | Nearly-sorted on most frames; near-O(n) typical |
| Dirty flag | Only re-sort when something moves enough to change order |
| Spatial buckets | Pre-group by screen region; sort within buckets |
| Index arrays | Sort an index array, not the objects themselves; preserves object data layout |
| Bucket per scanline | Ultimate-Y-precision spatial bucket; common in 16-bit games |
Edge cases
| Situation | Solution |
|---|---|
| Same Y position | Use X position as tiebreaker (left-most first) or stable sort by object ID |
| Large sprites | Use anchor point — usually the sprite's feet, not its centre |
| Jumping characters | Sort by ground Y (where the character would land), not the sprite's current Y while airborne — keeps the character behind the same objects when jumping in place |
| Flying objects | Either separate layer (always-on-top) or use the ground-shadow's Y |
| Multi-tile objects | Use the closest-to-camera point (the bottom edge of the object) |
Modern applications
| Context | Usage |
|---|---|
| Unity 2D | Sorting layers + Y-based custom axis on the Sprite Renderer |
| Godot | y_sort_enabled on Node2D containers |
| Unreal | Custom Z-axis or sort group with Slate UI |
| Isometric games | Hades, Diablo — Y-sort within the isometric tile |
| Transparency sorting (3D) | Even with depth buffers, transparent objects need explicit back-to-front sorting |
Legacy
Y-depth sorting remains fundamental — modern engines automate it via sorting layers and built-in features, but understanding the principle helps debug visual layering issues, optimise rendering in sprite-heavy games, and properly sort transparent objects in 3D rendering pipelines. Every game with a "weird sprite layering bug" has eventually traced it back to Y-sort logic.