Making 'Layered', My First Game

Published on April 17, 2025 by Caer

This April, I competed in Ludum Dare, a biannual event that challenges devs to make an entire game in a weekend. This is a log of how I made my first game, Layered, during Ludum Dare #57...with my own 2.5D Rust game engine.


Try Layered in the browser and share your feedback.

Record Scratch You're probably wondering how I got here...

For me, making code that interacts with a world is so much fun: Over the past few years, I've visited the WPI Swarm Technology Lab several times to test my new data platform (Codas) on real robot swarms.

However, I don't always have access to a lab full of robots, and I don't love the developer experience of simulators (like ARGoS) or game engines (like Bevy).

So...earlier this year, I decided to make my own 2.5D game engine in Rust, building off of Macroquad and deeply integrating Codas. And what better way to test this engine than to enter Ludum Dare and make a game with it?

Concept: Deep Networks, Dense Layers

The theme for Ludum Dare #57 was depths.

Although there were more than a few developers talking about ocean and cavern-inspired concepts, a different concept popped into my mind: Deep Neural Networks. Deep neural networks are comprised of sequential layers of "neurons" (math functions), enabling them to power models like ChatGPT.

Picture of me sitting with an early 'Layered' prototype and a book on neural networks.

During the jam's first ~24 hours, I daydreamt at my local eatery while pondering how I could create a gameplay mechanic based on neural networks:

  • Could players take the role of a single "data" traversing the network?

  • What if the "data" activates different neurons in the network, similarly to how neurons are "activated" when neural networks train?

  • How does "data" move between the layers of the network?

Building off these ideas, I settled on the core mechanic: Physically connecting neurons in the layers of a neural network.

Note: The neurons within the same layer of a real neural network typically don't connect to one another directly, so this game concept isn't technically accurate...but fun > everything_else.

Although my art style (2.5D Axonometric) restricts the playing field to an X/Y coordinate system, I realized I could stack layers on top of each other, visualizing the flow of data across layers.

Note: "Axonometric" is a family of art styles including Isometric, Dimetric, and Trimetric projections. Although most 2.5D games are described as isometric, they're actually dimetric: While visually similar, dimetric cubes can conveniently be rendered in a 2:1 aspect ratio, whereas isometric cubes have a trickier 1.7:1 aspect ratio.

In essence, my concept was puzzles inspired by deep neural networks.

Making the Layers

Layers are the foundation of Layered. Each layer is a self-contained 2D puzzle, visually inspired by something unexpected: My Mac's screen saver.

Screenshot of the retro pixel art MacOS Sequoia wallpaper

Introduced in MacOS Sequoia, this screen saver renders Susan Kare's iconic pixel art on a living axonometric plane that animates individual pixels.

Pixel Design™

These animations gave me an idea: What if layers were pixel art? Fueled by this inspiration, I decided to design each layer using 16x16 pixel art:

Picture of a 16x16 image describing a single Layered level

Through a process I now cheekily call Pixel Design™, this art is converted by my engine into an in-game layer. Each color in the art defines what gets rendered in-game:

  • Orange pixels define puzzle checkpoints.

  • Blue pixels define threat emitters.

  • Magenta pixels define the player spawn point.

  • Dark pixels create wall or obstacle tiles.

  • All other pixels create empty floor tiles.

The pixel art above results in this rendered layer:

Picture of the Layered level rendered from the previous 16x16 image

What I love about Pixel Design™ is that anyone who can open MS Paint (and friends) can quickly create new layers—no specialized tools needed.

Connecting the Network

To represent the connections between neurons in a single layer, I settled on the idea of checkpoints (the orange pixels in each layer's design).

The players' goal is to form a continuous connection between all the checkpoints in a layer, with a twist: The connection is interrupted by walls and obstacles.

Video showing how checkpoint connections in Layered are blocked by wall tiles

To detect these collisions, I implemented a super naive version of 2D ray tracing based on Bresenham's Line Drawing Algorithm.

During each key frame, this algorithm finds all tiles which intersect a straight line between the player and the most recently reached checkpoint (we'll call these line_tiles). Then, we iterate over all the tiles in order, starting from the tile furthest from the player:

  • If the tile contains an obstacle, we stop iterating and set broken_line = true.

  • Otherwise, we recolor the tile to magenta.

If the player intersects a new checkpoint tile in the current frame and broken_line == false, we flood-fill the checkpoint with magenta and cache the current line_tiles, using them to draw connections between all previously reached checkpoints.

If no orange tiles (checkpoints) remain on the layer at the end of the frame, the layer is complete, and we transition to the start of the next layer.

Making Waves

After wrapping up the layers and connections mechanic, I wanted to add visual excitement and mechanical threat to Layered. I forget exactly what inspired me, but I settled on the idea of expanding waves:

Video showing how the waves emitted by "threat" tiles in Layered are occluded by wall tiles

During each key frame, and for each blue tile (threat origin) on the layer, I used the Midpoint Circle Algorithm to find all tiles which intersect a circle with a frame-dependent radius centered at the threat.

If the player intersects any blue tiles in the current frame, we reset the entire layer and transition to the start of the current layer.

Occluding the Wave Front

I was really happy with how the expanding wave turned out, but I quickly hit a snag: Waves would pass through obstacles, which didn't seem very nice, since neither the player nor connections can pass through them.

To solve this issue, during each frame, I use my naive ray tracing to find all tiles between each tile on the "wave front" and the threat origin. If any of these tiles contains an obstacle, that section (tile) of the wave front is marked as "occluded", and doesn't get rendered.

Though simple, this logic results in a fairly convincing illusion of sections of the wave being "deleted" or occluded by obstacles.

Polishing the Aesthetic

Satisfied with the mechanics I implemented, I spent the last few hours of the event polishing the visuals and audio.

My first pass of polish focused on scene transitions: Instead of abruptly cutting when the player finishes a layer or hits a threat, I added a simple fade-out/fade-in between layers. As a finishing touch, I color-code blank screen based on context:

  • When resetting a layer, the screen is magenta.

  • When hitting a threat, the screen is blue.

  • In all other cases, the screen is neutral-dark.

Finally, I needed some audio! My goal was for the entire game to be calm, meditative, and happy. But alas, I'm no audio engineer—so I outsourced some gentle music (by MooMarMouse) and cute sound effects (by JDWasabi).

Feedback

The Feedback from my fellow Ludum Dare developers was overwhelmingly positive. Consistently, players:

  • Enjoyed the puzzles and the way I designed the introductory layers to gradually introduce the player to each mechanic.

  • Loved the art and audio, commenting that it felt polished and relaxing.

  • Celebrated that I built Layered using my own Rust game engine.

The most common critique I received was that players wanted to keep playing, but there weren't enough layers. This critique is such a precious one to receive, and it's inspired me to keep developing Layered beyond the game jam. ♥

Reflections

As someone who spends most of their days building "invisible" software systems (think: the plumbing that holds cloud services together), it was so fun to make something as visual as a game.

At the start of the game jam, my "Little Rust engine that could" really only contained features for rendering and converting between world-space X/Y coordinates and screen-space axonometric coordinates. By the end of the jam, I'd extended the engine to:

  • Support Pixel Design™ by generating tilemaps from PNGs. ec4354dc

  • Identify tiles intersecting a line between two coordinates. 8044ce61

  • Support cursor-based tile selection as an optional module. ee774b4c

  • Support a state-machine for swapping between tilemaps. d62eca9d

And for Layered specifically, I:

  • Implemented an option to make player motion relative to the viewport (screen) instead of the axonometric grid. 35e57e4d

  • Used tile colors as a virtual stigmergy for game state. 446b7fdd

  • Implemented pulsing wave emitters with ray-traced occlusion culling (I'm really happy with this one). 105a0fc3

Most glaringly, I didn't take the time to integrate codas into the engine for state management or codas-flow for frame-to-frame event handling.

Looking ahead, for both Layered and it's engine, I want to:

  1. Decouple the game logic from the engine logic, cleaning up the various stylistic crimes I committed in the heat of the game jam.

  2. Introduce the codas and codas-flow crates, using them to manage state transitions and frame-to-frame event handling.

  3. Decouple the Pixel Design™ functionality from the game's color palette so that it can be used in other game contexts.

  4. Devise a visual for data flowing across completed layers, realizing my original concept of connecting an entire neural network.

  5. Design many, many more layers for the game (and, perhaps, a "win condition" of some kind).

As a maker, my goal is to craft things that make others smile—and I think I can hone Layered into one of those things.


Did I miss anything or get a detail wrong? Let me know on !

all views expressed are my own, and
don't reflect those of my clients or employers

made by me with rust
© With Caer, LLC 2025