🎬The Past…
Does anyone remember downloading J2ME games via premium SMS, often discovered in the back pages of paper magazines? That’s where my professional game development journey began.
Here are some screenshots from games programmed by me that I was able to find in the Internet Archive.
At the time, Java games dominated the European market, while in the US, BREW and native C solutions were more common. Most devices had strict application size limits, typically around 64-256 kB. Yes, kilobytes.
Let’s take a step back in time. Here are some of the techniques we used to overcome those limitations.
🎨The Art…
We relied heavily on PNG as our primary image format because it supports indexed color palettes with different bit depths. Fewer bits per color index meant smaller files. It wasn’t unusual to work with palettes limited to 256(8bpp), 16(4bpp), or even 4(2bpp) colors. To compensate for the reduced color range, we used dithering extensively.
File2 → 256 Colors Indexed Palette (21.28kB)
File3 → 16 Colors Indexed Palette (3.91kB)
File4 → 4 Colors Indexed Palette (2.48kB)
File size optimization was critical. PNG contains many optional data chunks, which give us plenty of room for trimming unnecessary data. Tools like PNGOUT became essential in our workflow. Later on, IrfanView with the PNGOUT plugin became one of our best friends for squeezing every possible byte out of our assets.
- File1: 28.4kB → 27.97kB (98%)
- File2: 21.28kB → 10.79kB (51%)
- File3: 3.91kB → 3.55kB (91%)
- File4: 2.48kB → 2.17kB (87%)
Another common technique was using a single PNG image with multiple interchangeable palettes. By swapping only the palette chunk inside the PNG file, recalculating the checksum, and reusing the same image data, we could generate multiple color variations without duplicating the file itself.
16 Colors Indexed Palette → Exchanging only colors palette.
When creating sprite sheets, it was often beneficial to make them taller rather than wider. PNG uses row-based filtering before compression, which helps the compression algorithm achieve better compression ratios. More rows meant more opportunities for redundancy across lines, resulting in smaller files.
Another optimization technique was splitting images to remove unused empty regions. There were no existing tools, so you usually had to program your own.
If you were clever, you would build a set of tools that allowed you to reuse the same elements in different animations. By combining and repositioning these elements, it was possible to construct full animations while keeping asset sizes small. All images could then be merged into a single sprite sheet to minimize file size. Later, as the industry transitioned from J2ME to the early iOS era, using big sprite sheet textures became the preferred approach, but that is another story…
We also merged multiple game data files into a single archive. Since the final JAR build was compressed using ZIP, consolidating assets improved overall compression efficiency compared to compressing many small files separately. The more files you merge, the smaller the result, especially for non-image files.
- Zip(File1, File2, File3, File4, File5, File6) → 74.3kB
- Zip(File1 + File2 + File3 + File4 + File5 + File6) → 73.6kB(99%)
💻The Code…
On the code side, we aggressively optimized compiled class sizes. Shrinking + obfuscation was standard practice. Beyond protecting the code, obfuscators renamed classes and variables to the shortest possible identifiers and removed unused references and code. Every byte mattered and these techniques added up. ProGuard was your best friend.
Original Java Code
Calculator.class → 1105B
After Shrinking + Obfuscation
a.class → 460B (42%)
🎧The Audio…
Audio required the same level of discipline. We reduced the sampling rate of WAV files to the absolute minimum acceptable quality. In many cases, we avoided WAV files altogether and relied on MIDI instead. It wasn’t about fidelity, it was about fitting everything into a space smaller than a modern icon file. For music midi format was a must.
- Bullet Shot - File1(44.1kHz, wav, stereo) → 3.48kB
- Bullet Shot - File2(16kHz, wav, mono) → 0.7kB
- Bullet Shot - File3(mid) → 34B | yes, 34 bytes only!
🎯The Present…
Many of those optimization techniques may sound like relics of the past, but the mindset behind them remains relevant. Modern games especially in VR still demand performance awareness, memory discipline, and smart asset management. Working under such extreme limitations early in my career taught me to pay attention to every detail and to treat optimization not as a final step, but as an integral part of the development process.







Top comments (0)