---
title: A 50 FPS Game Engine on an ATtiny85
description: Building a tiny embedded game engine around an ATtiny85V, OLED display, IR input, EEPROM persistence, and a custom PCB.
date: 2026-05-06
projectPeriod: 'Spring 2020'
thumbnail:
  src: ./_assets/ad-astra.jpg
  alt: The Ad Astra game running on a small OLED display.
tags: ['embedded', 'games', 'systems']
selected: true
featuredOrder: 5
project: ad-astra
role: Hardware and firmware author
stack: ['C', 'ATtiny85V', 'OLED', 'EEPROM', 'PCB design']
scale: 8-bit microcontroller, 8 MHz clock, 15-20 ms maximum frame times during gameplay
outcome: A working low-power handheld game engine and game built from the circuit board up
audience: technical
links:
  - label: Source
    type: source
    url: https://github.com/schmelczer/ad_astra
media:
  - type: video
    poster: ./_assets/ad-astra.jpg
    webm: /media/video/ad_astra.webm
    mp4: /media/video/ad_astra.mp4
    alt: Video demonstration of the embedded game running on a small OLED display.
    caption: The game engine ran on an ATtiny85V with an OLED display and IR input.
---

Ad Astra came from wanting to combine graphics and microcontrollers without hiding behind a large development board. The result was a small embedded game engine and game built around an ATtiny85V, an OLED display, IR input, EEPROM persistence, and a custom PCB.

The fun part was that every layer mattered. The circuit, display driver, memory layout, object model, sprite tooling, and game loop all had to fit inside a tiny system.

## The Problem

The hardware setup was intentionally constrained: an ATtiny85V, a D096-12864-SPI7 OLED display, a TSOP4838 IR receiver, and a 3.3V regulator. The system was low power, with peak consumption around 31 mW at full brightness and a standby mode around 1.5 mA.

Those numbers made the project feel physical. Performance was not an abstract target. Every frame and every byte had a cost.

## Constraints

The engine ran at 8 MHz on an 8-bit ALU. That meant the display driver and game loop had to avoid expensive generality.

Even the programming model needed restraint. I wrote the firmware in C, but used a balance of structured and object-oriented ideas to keep game object behavior manageable without paying for a runtime that did not exist.

## Design

The display driver was the most performance-sensitive layer. I used SIMD-like techniques on the 8-bit ALU to process four pixels at once. That helped keep maximum frame times between 15 and 20 milliseconds during gameplay, so the lowest gameplay frame rate stayed above 50 FPS.

For game objects, I used prototype-based inheritance. It was a pragmatic way to reuse behavior while keeping the implementation simple enough for the target.

Persistent state used the built-in EEPROM with an atomic commit approach. Sprite data also lived in EEPROM, and I wrote scripts to convert PNG sprites into C array definitions so assets could move into firmware cleanly.

## What Worked

The project worked because the abstraction level stayed close to the hardware. The engine had reusable pieces, but none of them pretended the platform was larger than it was.

The custom PCB also changed the project. Once the system had a real board, bugs felt less like software inconveniences and more like design consequences. That made the final result much more satisfying.

## What I Would Change

Today I would write a more explicit development log around the display driver and persistence layer. Those are the parts that still feel technically interesting, and they deserve diagrams and measurements.

I would also add a small emulator or host-side harness. Debugging firmware directly on constrained hardware is useful, but a fast feedback loop would have made the engine easier to evolve.
