Date: Thursday April 16th, 2026
Time: 10:45:39 AM MST
From: @vga256
Keywords: exigy, data
Subject: doing a computing science degree the hard way, or how Tim Cain saved the day
Time: 10:45:39 AM MST
From: @vga256
Keywords: exigy, data
Subject: doing a computing science degree the hard way, or how Tim Cain saved the day
Tim Cain once said that the worst, most time consuming part of his jobs working on Fallout and Arcanum, was writing UI code. Unsurprisingly, most coders drag and drop in UI libraries like Dear ImGui and call it a week. "Why reinvent the wheel? UI coding is like - a 1970s problem!"
The thing is, I like UI coding. It's like building a Ferrari by starting with a pile of ore, a blast furnace, and a hammer. Why write your own UI library? For the same reason web designers manually write CSS instead of templating: you want something unique or you want to understand the way the system works from the ground up. There is no better way of understanding a system than building it yourself.
I'm the kind of programmer/designer that works visually. I begin by sketching out how things should look on paper (5 minutes), and draw the object parts in Aseprite (2 hours), and then write code to produce the intended behaviour (20 hours). That last step is a killer, because I'm usually working in under-explored territory. Sure, if you took a 4th year UI Systems class in university, you might be introduced to some of the fundamental concepts involved in Immediate Mode GUIs. But for self-taught coders like myself, writing your own libraries can be a monumental task. Just try searching for the term Immediate Mode GUI - the descriptions are full of industry jargon and assume that you've already got a Bachelor of Science in CS.
The funny thing is that after a few months - back in December of 2024 - I had some of the basics of a UI library working! Stacked windows, drag-and-drop, window frames, titlebars, windows with sub-elements like buttons and textboxes. It turns out that while debugging GUIs is time intensive, creating the windowing manager and window elements is a piece of cake. Creating new window objects like scrollbars and drop-down menus was just a matter of cloning a UIElement, and adding a few new behaviours to it. By mid-2025, I had created two dozen types of window elements - the kind you'd expect in any application or game. The editor is a complete WYSIWYG interface: you drag and drop elements into an empty window, and without compiling or any kind of heavy duty coding, you've got a working dialog box or window with text input.
Throughout that entire design and implementation process I never once worried about "data structures". Data Structures, to me, was the title of a shitty 2nd year Computing Science class that you fell asleep in every morning at 8am. The entire reason I chose Lua over C or C++ was because I didn't want to worry about data structures: I wanted basic variables and references without worrying about referencing and dereferencing pointers.
This fuck-Data-Structures policy turned out to be a fatal mistake about a year into the project :*) Once I had a basic windowing system working, with some basic game objects like Tiles and Entities (characters/enemies), I could build a basic Microsoft Entertainment Pack-style game like Rodent's Revenge in about five minutes. It was amazing, and validated the idea of a new Rapid Application Development program for the mid 2020s.
Except, you couldn't save or load your project. I began searching for saving and loading methods for Löve2D: it didn't have any. Okay, fine - I'll just use the built-in saving and loading functions that Lua must hav---- it doesn't have any either? WTF?
It turns out that the term I was looking for was "serialization": turning an object into a format that can be easily stored in a file. I had naïvely assumed that serializing was a solved-problem thanks to 50+ years of computing science theory. In many ways it is a solved problem: almost any modern programming language has serialization libraries that you point your data at, and it creates a binary or JSON representation of in a file. Loading that data is just as easy.
For that reason, serializing and deserializing data is one of those topics that no one talks about anymore. With Lua, there are a couple of excellent serialization libraries like Binser that can turn your game data into files.
For example, imagine that you've got a player object that looks like this:
Player.name = "Javin"
Player.score = 29490
Player.level = 3
The serializing library has to figure out how to convert the string "Javin", and the numbers 29490 and 3 into a chain of bits in a file. It also has to keep track of the object's structure too: Player has .name, .score, and .level fields. After one look at how Binser turns code into binary data, I immediately knew I was over my head: there is no way in hell I was going to write my own serialization library. "Fine! I'll just use Binser to do the dirty work!"
Serialization libraries do a great job with simple data like the above. One or two lines, and you can save and load the Player's name, score and level. Woohoo!
Except: there are certain kinds of data that serialization libraries have no idea what to do with.
What if the Player object looks like this?
Player.currentEnemy = OrcObject3
Player.currentTarget = Player
Player.currentSprite = PlayerAnimation.standing
In this example, OrcObject3 isn't a text string or a number. It's a game object, which has its own unique properties and fields. The serializer looks at this and says "Hey, no prob. I'll recursively dig into OrcObject3, and save its data too!" Except: you didn't want it to save a new OrcObject - you wanted it to save a LINK (a reference, a pointer) to OrcObject3.
And then, the serializer hits the next line: it tries to save the Player as currentTarget. The serializer sees that Player is an object, and recursively digs into Player to save its data. Now you've got an infinite loop from hell. The serializer may or may not be able to deal with this. Let's say that it does recognize infinite loops, and makes it to the third line.
PlayerAnimation.standing turns out to be a Löve2D sprite - an internal data type specific to Löve2D - composed of complex RGBA data. It's not a text string or a number. The serializer takes one look at this and says "Look, I'm sure this data is important, but I have NO idea what this is. I can't save it, or load it." The serializer either skips over it (now you've got lost data), or just dies completely with an error. (Binser, for what it's worth, dies here.)
It turns out that all serialization libraries can't handle complex data because they're designed to handle only a few standard things: booleans, floats, text, numbers and functions. If you've got anything more complex than that, you're own your own.
Now imagine just how much data is in a Window with sub-objects like Buttons and Scrollbars. It's incredibly complex, and has all kinds of weird data in it, including bitmaps and links to other objects.
After a few weeks of banging my head against a wall I reached the conclusion that I was out of my depth as a programmer. I had to hire someone to do the job, and found a friend who was excited by the prospect of dealing with low-level data structures like bits and bytes. They were incredibly talented, creative, and - unfortunately - as it turns out, in love with C. The serializer I got after a few months of their hard work was well-commented and well-organized, but even less functional than Binser, written in C, and didn't provide a way of saving any of the complex Windowing data I needed.
Beyond frustrated, I set the problem aside for a year, as if it would solve itself. I built out more of Exigy's UI and engine capabilities, adding cool things like support for Isometric perspectives and AABB (Axis-Aligned Bounding Box) collision detection.
About a month ago I got fed up with myself and decided to do something about it: I'd research the living hell out of serialization-for-games and come up with my own solution. I read Mike McShaffrey's excellent tome Game Coding Complete. I read through every stackexchange article I could find on how games save data. I dug through forums and every web article imaginable, and no one really had a general solution to saving game data. User @kikito lamented, "I have yet not found a satisfying way to serialize/deserialize classes (or tables) in a generic way". Really?
It's 2026: can't you just point a serializer at any object and say, "save this to a file for me?"
It turns out that I had made a fundamental assumption about computers and programming that any 2nd year Data Structures course professor would have pointed out in 5 seconds: "just save this random pile of bits to a file please" doesn't exist. The data has to be pre-organized, pre-digested first. The serializer has to be able to interpret the structure of the file, and make very smart decisions about what to save, and how to save it. A serializer is a little compiler. And like any compiler, it runs on the GIGO principle: Garbage In, Garbage Out. I was still stuck, but at least I knew why I was stuck.
What broke the stalemate was a sick-day that I spent on the couch watching Tim Cain's videos about his game development memories. In this video on Loading & Saving, Tim dropped this tiny, insignificant hint: "One of the first things I do when I write any new programming class is write its saver and the loader."
Wait. Every object class is supposed to have its own custom saver and loader?
Suddenly, the entire picture fell together. A Player object isn't supposed to just have a bunch of properties like its name, score, and current enemy. It's also supposed to tell the engine how each of those properties is supposed to be stored to and loaded from a file. There's supposed to be a Player.save() and Player.load(), and an Orc.save() and Orc.load(), and those methods won't be the same for every kind of game object, because every object class has different kinds of properties.
Even better, I stumbled on another hint from a game programmer (which unfortunately I've lost the link to) who said that they save their entire game world by starting at the root World, and recursively explore every branch and twig, saving objects along the way. Loading the world works by the exact same process in reverse. A tree!
I won't lie. It still took 3 weeks of writing some very difficult code to make any of this happen. But finally, after 1.5 years of dead ends, Exigy can save and load the game and/or window state. Maybe I'll write about the particulars of how that works in a future article. For now, I'm just happy to recognize this as a major milestone in the project.
The thing is, I like UI coding. It's like building a Ferrari by starting with a pile of ore, a blast furnace, and a hammer. Why write your own UI library? For the same reason web designers manually write CSS instead of templating: you want something unique or you want to understand the way the system works from the ground up. There is no better way of understanding a system than building it yourself.
I'm the kind of programmer/designer that works visually. I begin by sketching out how things should look on paper (5 minutes), and draw the object parts in Aseprite (2 hours), and then write code to produce the intended behaviour (20 hours). That last step is a killer, because I'm usually working in under-explored territory. Sure, if you took a 4th year UI Systems class in university, you might be introduced to some of the fundamental concepts involved in Immediate Mode GUIs. But for self-taught coders like myself, writing your own libraries can be a monumental task. Just try searching for the term Immediate Mode GUI - the descriptions are full of industry jargon and assume that you've already got a Bachelor of Science in CS.
The funny thing is that after a few months - back in December of 2024 - I had some of the basics of a UI library working! Stacked windows, drag-and-drop, window frames, titlebars, windows with sub-elements like buttons and textboxes. It turns out that while debugging GUIs is time intensive, creating the windowing manager and window elements is a piece of cake. Creating new window objects like scrollbars and drop-down menus was just a matter of cloning a UIElement, and adding a few new behaviours to it. By mid-2025, I had created two dozen types of window elements - the kind you'd expect in any application or game. The editor is a complete WYSIWYG interface: you drag and drop elements into an empty window, and without compiling or any kind of heavy duty coding, you've got a working dialog box or window with text input.
Throughout that entire design and implementation process I never once worried about "data structures". Data Structures, to me, was the title of a shitty 2nd year Computing Science class that you fell asleep in every morning at 8am. The entire reason I chose Lua over C or C++ was because I didn't want to worry about data structures: I wanted basic variables and references without worrying about referencing and dereferencing pointers.
This fuck-Data-Structures policy turned out to be a fatal mistake about a year into the project :*) Once I had a basic windowing system working, with some basic game objects like Tiles and Entities (characters/enemies), I could build a basic Microsoft Entertainment Pack-style game like Rodent's Revenge in about five minutes. It was amazing, and validated the idea of a new Rapid Application Development program for the mid 2020s.
Except, you couldn't save or load your project. I began searching for saving and loading methods for Löve2D: it didn't have any. Okay, fine - I'll just use the built-in saving and loading functions that Lua must hav---- it doesn't have any either? WTF?
It turns out that the term I was looking for was "serialization": turning an object into a format that can be easily stored in a file. I had naïvely assumed that serializing was a solved-problem thanks to 50+ years of computing science theory. In many ways it is a solved problem: almost any modern programming language has serialization libraries that you point your data at, and it creates a binary or JSON representation of in a file. Loading that data is just as easy.
For that reason, serializing and deserializing data is one of those topics that no one talks about anymore. With Lua, there are a couple of excellent serialization libraries like Binser that can turn your game data into files.
For example, imagine that you've got a player object that looks like this:
Player.name = "Javin"
Player.score = 29490
Player.level = 3
The serializing library has to figure out how to convert the string "Javin", and the numbers 29490 and 3 into a chain of bits in a file. It also has to keep track of the object's structure too: Player has .name, .score, and .level fields. After one look at how Binser turns code into binary data, I immediately knew I was over my head: there is no way in hell I was going to write my own serialization library. "Fine! I'll just use Binser to do the dirty work!"
Serialization libraries do a great job with simple data like the above. One or two lines, and you can save and load the Player's name, score and level. Woohoo!
Except: there are certain kinds of data that serialization libraries have no idea what to do with.
What if the Player object looks like this?
Player.currentEnemy = OrcObject3
Player.currentTarget = Player
Player.currentSprite = PlayerAnimation.standing
In this example, OrcObject3 isn't a text string or a number. It's a game object, which has its own unique properties and fields. The serializer looks at this and says "Hey, no prob. I'll recursively dig into OrcObject3, and save its data too!" Except: you didn't want it to save a new OrcObject - you wanted it to save a LINK (a reference, a pointer) to OrcObject3.
And then, the serializer hits the next line: it tries to save the Player as currentTarget. The serializer sees that Player is an object, and recursively digs into Player to save its data. Now you've got an infinite loop from hell. The serializer may or may not be able to deal with this. Let's say that it does recognize infinite loops, and makes it to the third line.
PlayerAnimation.standing turns out to be a Löve2D sprite - an internal data type specific to Löve2D - composed of complex RGBA data. It's not a text string or a number. The serializer takes one look at this and says "Look, I'm sure this data is important, but I have NO idea what this is. I can't save it, or load it." The serializer either skips over it (now you've got lost data), or just dies completely with an error. (Binser, for what it's worth, dies here.)
It turns out that all serialization libraries can't handle complex data because they're designed to handle only a few standard things: booleans, floats, text, numbers and functions. If you've got anything more complex than that, you're own your own.
Now imagine just how much data is in a Window with sub-objects like Buttons and Scrollbars. It's incredibly complex, and has all kinds of weird data in it, including bitmaps and links to other objects.
After a few weeks of banging my head against a wall I reached the conclusion that I was out of my depth as a programmer. I had to hire someone to do the job, and found a friend who was excited by the prospect of dealing with low-level data structures like bits and bytes. They were incredibly talented, creative, and - unfortunately - as it turns out, in love with C. The serializer I got after a few months of their hard work was well-commented and well-organized, but even less functional than Binser, written in C, and didn't provide a way of saving any of the complex Windowing data I needed.
Beyond frustrated, I set the problem aside for a year, as if it would solve itself. I built out more of Exigy's UI and engine capabilities, adding cool things like support for Isometric perspectives and AABB (Axis-Aligned Bounding Box) collision detection.
About a month ago I got fed up with myself and decided to do something about it: I'd research the living hell out of serialization-for-games and come up with my own solution. I read Mike McShaffrey's excellent tome Game Coding Complete. I read through every stackexchange article I could find on how games save data. I dug through forums and every web article imaginable, and no one really had a general solution to saving game data. User @kikito lamented, "I have yet not found a satisfying way to serialize/deserialize classes (or tables) in a generic way". Really?
It's 2026: can't you just point a serializer at any object and say, "save this to a file for me?"
It turns out that I had made a fundamental assumption about computers and programming that any 2nd year Data Structures course professor would have pointed out in 5 seconds: "just save this random pile of bits to a file please" doesn't exist. The data has to be pre-organized, pre-digested first. The serializer has to be able to interpret the structure of the file, and make very smart decisions about what to save, and how to save it. A serializer is a little compiler. And like any compiler, it runs on the GIGO principle: Garbage In, Garbage Out. I was still stuck, but at least I knew why I was stuck.
What broke the stalemate was a sick-day that I spent on the couch watching Tim Cain's videos about his game development memories. In this video on Loading & Saving, Tim dropped this tiny, insignificant hint: "One of the first things I do when I write any new programming class is write its saver and the loader."
Wait. Every object class is supposed to have its own custom saver and loader?
Suddenly, the entire picture fell together. A Player object isn't supposed to just have a bunch of properties like its name, score, and current enemy. It's also supposed to tell the engine how each of those properties is supposed to be stored to and loaded from a file. There's supposed to be a Player.save() and Player.load(), and an Orc.save() and Orc.load(), and those methods won't be the same for every kind of game object, because every object class has different kinds of properties.
Even better, I stumbled on another hint from a game programmer (which unfortunately I've lost the link to) who said that they save their entire game world by starting at the root World, and recursively explore every branch and twig, saving objects along the way. Loading the world works by the exact same process in reverse. A tree!
I won't lie. It still took 3 weeks of writing some very difficult code to make any of this happen. But finally, after 1.5 years of dead ends, Exigy can save and load the game and/or window state. Maybe I'll write about the particulars of how that works in a future article. For now, I'm just happy to recognize this as a major milestone in the project.






Mercifully, Chahi provides a quick reference for the editor. Here we can see just how completely functional the interface is: you can do everything on the main screen.
