Coding in 12 Kilobytes: How Nokia Optimized Snake’s Tail Memory Loop
Inside the 1997 Nokia 6110 software design: How Taneli Armanto compressed coordinate data to squeeze a long, winding snake into a handful of microchip bytes.
Introduction: The Golden Age of Embedded Optimization
Today, a modern smartphone game can easily consume several gigabytes of storage, utilizing thousands of megabytes of RAM just to render basic menus. We live in an era of abundant computational hardware. But in 1997, the landscape of mobile technology was unrecognizable. When Nokia design engineer Taneli Armanto was asked to program a simple game for the upcoming Nokia 6110 mobile phone, he was handed a set of constraints that would terrify a modern developer: the entire operating system, cellular connection stack, and user interface were shared on a tiny ROM. The total budget allocated for games was exactly 12 Kilobytes of ROM, and the available system RAM for runtime game states was measured not in megabytes, but in bytes.
Armanto wanted to create an adaptation of the classic grid-based trail game, Snake. The core challenge lay in the physics of a growing snake. As the player consumes food, the snake's tail grows longer, potentially winding around a 20-by-9 cell display. If a developer represents this winding body by storing the absolute X and Y coordinates of every single segment, the RAM buffers will quickly overflow, causing a system crash. This article details the hardware profile of early Nokia microcontrollers, the mathematical genius of Armanto’s circular ring buffers, and the bit-level compression tricks that allowed a snake of infinite theoretical length to traverse a mobile screen in just a few dozen bytes.
The Nokia 6110 Hardware Profile
The Nokia 6110 was one of the first phones to run on an early ARM7TDMI 32-bit RISC processor, but to save battery life and reduce manufacturing costs, the processor was underclocked to operate at a crawl. The monochromatic liquid crystal display (LCD) featured a resolution of exactly 84 by 48 pixels, which was divided into a gameplay matrix of 20 columns by 9 rows for Snake.
Because the RAM on the system was extremely volatile and shared with the active cellular transceiver (which required constant buffering of incoming carrier signals), the game was allocated a static heap of under 100 bytes. Storing standard integers (which occupy 4 bytes each in 32-bit architectures) was an absolute luxury. If Armanto had used a simple array of 32-bit coordinates to track a 100-segment snake, he would have consumed 800 bytes—nearly eight times the entire RAM budget! He needed a highly compressed data structure.
Optimization Strategy 1: The Circular Ring Buffer
To bypass the need to constantly shift elements in an array at every clock tick, Armanto implemented a classical computer science data structure: a circular buffer (also known as a ring buffer or cyclic queue).
Instead of dynamically allocating memory for the snake or shifting coordinates through a standard array (which requires a high-overhead memory copy operation at every frame), Armanto pre-allocated a static array of a fixed size, matching the maximum possible number of squares on the screen (20 columns x 9 rows = 180 cells). The array was treated as a closed circle, where the index after the final element wrapped around directly to the first element. Two pointer variables were maintained in RAM: a head pointer and a tail pointer.
When the snake moved one step forward, the system calculated the new head coordinate and wrote it to the array address currently pointed to by the head pointer. The head index was then incremented by one. If no food was consumed, the system read the coordinate at the tail pointer, erased that block from the screen memory, and incremented the tail index.
tail = (tail + 1) % MAX_CELLS;
This bypasses any array shifting routines. A 100-segment snake required exactly zero memory shifts; the system only updated two numbers in RAM, making the update routine incredibly fast and highly energy-efficient, protecting the Nokia battery life.
Optimization Strategy 2: 2-Bit Directional Vector Compression
While the circular buffer avoided shifting memory, storing coordinate pairs `(X, Y)` for each cell still consumed too much space. Armanto realized that he did not need to store the absolute position of every segment of the snake's body. The only absolute coordinate that truly mattered was the **head**. Since every body segment is connected to the next one, each segment can only ever sit in one of four directions relative to its neighbor: **Up, Right, Down, or Left**.
A direction can be represented using exactly 2 bits of information:
00= Up01= Right10= Down11= Left
By representing each body segment as a relative directional vector rather than a coordinate, Armanto achieved an extraordinary level of compression. A single 8-bit byte contains 8 bits, meaning it can store exactly four body segments. A 32-bit register could hold sixteen segments. Let's compare the memory footprint of these different techniques to see this efficiency:
| Data Representation Method | Bytes per Segment | RAM Required for 100 Segments | CPU Cycles per Move Tick | |
|---|---|---|---|---|
| Coordinate Array (32-bit X, Y integers) | 8 bytes (4 for X, 4 for Y) | 800 bytes | High (Shifting 100 array items) | Inefficient (RAM Overflow) |
| Coordinate Array (8-bit X, Y unsigned chars) | 2 bytes (1 for X, 1 for Y) | 200 bytes | Medium (Shifting 100 bytes) | Borderline (High RAM strain) |
| Circular Buffer of Coordinates (8-bit) | 2 bytes (1 for X, 1 for Y) | 360 bytes (static screen buffer) | Very Low (2 index updates) | Good (High CPU savings) |
| Bit-Compressed Directional Chain (2-bit relative) | 0.25 bytes (2 bits) | 25 bytes (Plus 2 bytes for Head) | Low (Bit shift and bitwise masking) | Perfect (Ultra-compressed) |
To render the snake, the system started at the absolute coordinate of the head, read the first 2-bit direction, traced back to find the second segment's position, read the next 2 bits, and continued tracing back along the chain until it reached the tail. This directional chain acted as a highly optimized, hardware-level linked list that lived entirely inside a tiny, 25-byte register block.
The Embedded Code: Deciphering the Bit Shift Loop
To read the relative body directions, the microprocessor used highly efficient bitwise operators. Let's look at a typical optimization routine in C showing how the 2-bit direction was extracted from the compressed byte array:
unsigned char get_segment_direction(unsigned char *buffer, int index) {
int byte_position = index / 4; // 4 segments per byte
int bit_offset = (index % 4) * 2; // 2 bits per segment
unsigned char target_byte = buffer[byte_position];
// Shift target bits to the lowest positions and mask out everything else
return (target_byte >> bit_offset) & 0x03;
}
This entire process required only three basic CPU instructions: a division (implemented as a right-shift by 2), an addition/multiplication, and a bitwise AND operation. For the underclocked ARM7TDMI chip, this math was processed in a single cycle, ensuring the snake moved smoothly with zero lag while consuming virtually no power.
Legacy and Modern Web Translation
Taneli Armanto’s optimizations on the Nokia 6110 are a masterclass in clean, mathematical software design. It proved that extreme constraints are not a barrier to brilliant gameplay—they are a catalyst for it. The circular ring buffer and bit-level compression routines remain beautiful examples of how hardware-level awareness can yield incredibly elegant code.
At YuvaMedia, our modern web-based Snake Game pays direct tribute to these classic optimization techniques. While modern browsers have megabytes of memory at their disposal, our underlying JavaScript engine is built with the same respect for performance. We structure our game coordinates and memory updates using high-efficiency queue structures that mirror Armanto's circular index logic, providing a lag-free, ultra-smooth, and responsive experience on both desktop screens and low-power mobile devices. Jump into our arena, feel the responsive feedback of an optimized engine, and see how long you can grow your snake today!