How Did Game Companies "Handcraft" Games Before Mature Engines?
The answer to this question is far more raw than most people imagine.
Today’s game developers open Unity or Unreal, and the editor is there, the physics engine is there, the rendering pipeline is there, the lighting system is there. Your job is to fill in content within a framework that’s already been built. That simply did not exist in the 1980s. There was no framework, no engine, no reusable infrastructure. If you wanted to make a game, you first had to build a machine that could run it, and then on that machine, using assembly language, you told it what color every single pixel should be.
It started with pixels.
Early game graphics were written directly into memory. Programmers needed to know the memory address corresponding to every position on the screen. To draw a point at a certain location, you wrote a value to that address — the value represented the color. To draw a line, you wrote a loop, telling the CPU to write at every Nth byte from this address to that address. To draw a character, you converted each row of pixels into hexadecimal numbers, wrote them in line by line, found the correct offset on the screen, and placed those numbers there.
This process is called “software rendering” today. Back then it had no name, because there was no alternative — it was just called “how the画面 came out.”
When Carmack was working on the first Wolfenstein game, he faced a specific problem: the IBM PC’s graphics architecture did not support smooth horizontal scrolling. The screen updated by refreshing the entire frame at once, causing tearing when the image moved — it looked like it was shaking. He spent a huge amount of time studying the GPU’s low-level registers and found a way to trick the hardware into doing something it didn’t natively support, simulating it in software so the画面 could scroll smoothly.
There were no documents for this, no前人经验. He just sat there, pored over hardware manuals, and figured it out bit by bit through trial and error.
This was the basic working condition of programmers in that era. You weren’t facing a system with a manual — you were facing the hardware itself. You had to know the hardware better than the hardware knew itself, and find space in its cracks.
Sound was an entirely different dimension of the problem.
The early PC’s sound system was a buzzer — the PC Speaker. It could only produce one sound: a beep. To play music with it, you had to control the vibration frequency of this buzzer, switching between different frequencies at extremely high speed, leveraging the persistence of human hearing to make it sound like a melody was playing. This technique is called PWM, pulse-width modulation. The principle is acoustic physics, the implementation is timing control precise to the microsecond. Get it wrong by even a little, and all you get is noise.
There were programmers at the time who专门研究 how to squeeze more layers of sound out of the PC Speaker. Their method was to switch between different frequencies in extremely short time windows, creating a pseudo-stereo effect. This technique was treated like black magic in the demo scene of the era. You took a buzzer that could only go “beep,” and made something that sounded close to music. Other programmers who heard it had no idea how you did it — because the原理 wasn’t intuitive. You needed an extremely precise understanding of timing control to pull it off.
When Adlib and Sound Blaster came out, they brought FM合成 sound. This was another system you had to learn from scratch. The principle of FM合成 is using the waveform of one frequency to modulate the waveform of another, producing complex timbres. Different modulation parameters produced different instrument sounds. Programmers had to tune the parameters themselves, test which combinations sounded like a piano, which like a violin, which like an explosion. There was no standard answer — it was all靠耳朵和经验. At the time, every company’s sound programmer had their own accumulated parameter tables. Those were trade secrets in the truest sense.
The design sense at the game logic level was another matter entirely.
No engine meant no ready-made collision detection, no ready-made physics simulation, no ready-made AI framework. If you wanted a character to stop when it reached a wall instead of walking through it, you had to write collision detection yourself, define what “collision” meant yourself, and decide how the character’s speed and position would change after the collision yourself.
Early game collision detection was simple and brutal: treat both the character and obstacles as rectangles, check whether the two rectangles overlap, and if they do, stop. This方案 was fast, but the problem was that real game characters aren’t rectangles — they’re sprites with shapes. There was always a gap between the rectangular collision box and the actual graphic. That’s why early games often had bullets that clearly missed but still registered as hits, or characters that stopped before they’d visibly touched the wall.
Players at the time didn’t call this a bug. They called it a game feature. Because they didn’t know it could be any other way.
Another thing worth mentioning: early PCs had extremely limited memory. 640KB was the hardware ceiling for a full decade. That number today can’t even hold a single screenshot. Programmers had to cram everything in the game — graphics, sound effects, maps, logic code — into that 640KB, and still leave room for runtime data.
How? Compression, chunked loading, and reuse.
Graphics data compression was its own discipline. Programmers had to invent their own compression algorithms, making graphic data as small as possible when stored and decompressing it instantly when needed. If the map was too big to fit, you split it into chunks, loaded the chunk the player was standing in, unloaded it when they left. Today this technique is called streaming. Back then it was called “no other way.”
Every byte in memory was fought for. When writing code, programmers constantly had to think: can I use a smaller data type for this variable? Can I use fewer temporary variables for this logic? Can I reduce the number of memory accesses in this loop? This way of thinking is largely unnecessary for today’s programmers. Modern hardware has enough memory headroom, and compilers are smart enough in optimization — you don’t need to count every byte.
But the programmers of that era carried the entire inner workings of the computer in their heads. From CPU instruction cycles to memory layout to GPU registers, they had to understand every layer, because every layer was a gap they might need to exploit.
Carmack once wrote a piece of code for computing the inverse square root, with a comment in it that read “what the fuck.” This code used a wildly counterintuitive bit manipulation trick to quickly approximate the inverse square root, far faster than the standard math library — all to save computation time in 3D rendering. This piece of code later became a legend in programmer culture, cited over and over, because it represented a way of thinking that has since disappeared: the method of squeezing performance out of mathematics and creativity within hardware constraints.
The game programmers of that era were closer to a hybrid of engineers and scientists. The problem they solved wasn’t just “how to implement this feature” — it was “can this feature be implemented at all within the physical limits of this machine?”
Those are two very different questions.
The first question has a standard answer: look up the documentation, call the API, follow the steps. The second question has no standard answer. You have to invent the answer yourself. Sometimes the answer exists, sometimes it doesn’t. You had to first figure out which case you were in, and then decide what to do.
Today’s game industry is a massive collaborative system. Engine companies, middleware companies, toolchain companies — every layer has someone working on it. Developers stand at the top of this system, focused on content itself. This is progress, it’s a good thing — it lets more people make games, and lets the scale and complexity of games far exceed what was possible in the past.
But that era of building games starting from pixels had something that no longer exists today.
You open the game, and you know where every point on the screen came from, how every sound was made, how every collision was judged. Because those were all written by you, line by line. There was nothing that someone else did for you.
This feeling has a name. It’s called total control.
So the greats who developed games back then — they were truly great. Truly remarkable.