01overview 2D · C++ · MCU + Desktop

Tiny enough
for an MCU.

A modular C++ 2D engine. Drive an LCD from an ESP32, run the same project on Windows through SDL3, or drop rendering entirely and use it as a headless component framework. Every part is a package you can leave out — the renderer included.

lang
C++17
packages
24
render
RGB565+
license
Apache 2.0
320 × 240 · RGB565 · rendered by the engine
02visual editor

Write the component.
The inspector builds itself.

Fields tagged DEKI_EXPORT are picked up at build time by the C++26 static-reflection codegen. That one tag is what drives the inspector widget, MessagePack serialization, undo, and asset resolution — there is no second place to register anything.

02.1

The whole window

Hierarchy and asset browser on the left, scene canvas and console in the middle, inspector on the right. Every panel is an ImGui dock — drag them wherever you want them.

The full Deki editor window: hierarchy and asset browser on the left, the scene canvas and console in the middle, the inspector on the right.
02.2

One tag, and the inspector follows

Bobber.h declares two floats and tags them DEKI_EXPORT. Nothing else registers them anywhere: the sliders on the right, their ranges, the undo entries and the MessagePack fields all come from that tag at build time.

Bobber.h // project/src
DEKI_CATEGORY("Demo")
DEKI_DESCRIPTION("Bobs the object up and down on a sine wave.")
class Bobber : public Deki::Component
{
public:
    DEKI_EXPORT DEKI_SLIDER(0, 64)
    float amplitudePx = 12.0f;

    DEKI_EXPORT DEKI_SLIDER(0, 4)
    float speedHz = 0.6f;

    DEKI_EXPORT DEKI_SLIDER(0, 1)
    float phase = 0.0f;

    void Awake() override;
    void Update() override;
};
Bobber.cpp // project/src
#include "Bobber.h"

#include <cmath>

void Bobber::Awake()
{
    if (Deki::Object* owner = GetOwner())
        m_BaseY = owner->GetY();
}

void Bobber::Update()
{
    Deki::Object* owner = GetOwner();
    if (!owner)
        return;

    m_ElapsedS += Deki::Time::GetDeltaTimeF() * 0.001f;

    // Positions are in meters; the exported amplitude is in pixels so
    // the inspector value matches what you see on a 320x240 panel.
    const float ppm = Deki::EngineSettings::Global().pixelsPerMeter;
    const float wave = std::sin((m_ElapsedS * speedHz + phase) * 6.283185307f);

    owner->SetY(m_BaseY + (amplitudePx / ppm) * wave);
}
The inspector for a selected coin: Transform, a Sprite Component with its sprite picker, tint colour and render mode, then the Bobber component exposing Amplitude Px, Speed Hz and Phase.
inspector · generated from the struct above
02.3

The scene tree

Ctrl-click and shift-click to multi-select, drag to reparent or reorder. Structure edits go through the same command history as value edits, so undo covers both.

Hierarchy panel showing the demo scene's object tree: Camera, Sky, Hills, Terrain, Platforms, Props, Pickups, Actors and HUD, with the Pickups group expanded and Coin1 selected.
02.4

Pixel-exact with the target

Not an approximation. Edit mode calls the same RenderToBuffer() as play mode and as a device build, at whatever the panel actually is — 320×240 here — so the editor, play mode and the board agree. Rendering is just one package: leave it out and the rest of the engine still runs.

Scene view: the 320 by 240 canvas showing the demo scene — a green alien on a grassy planet, coins, two floating platforms with a gem, a winged creature and a hearts-and-score HUD.
Full editor walkthrough →
03core engine

Everything you need.
Nothing you don't.

F.01

Components with reflection

One base class. A class deriving from Deki::Component that declares Awake / Start / Update runs; one that declares none is data. Tag a field DEKI_EXPORT and it appears in the inspector, serializes, and resolves its assets.

F.02

Visual editor

Dear ImGui + SDL3 + OpenGL. Hierarchy, inspector, asset browser, console, profiler, and undo that merges continuous edits into one entry.

F.03

Hot reload

Edit a component, focus the editor, and it rebuilds and swaps the package and plugin DLLs in place. No restart, no scene reload.

F.04

Software renderer

One QuadBlit pipeline, four output formats: RGB565, RGB565A8, RGB888, ARGB8888. Dirty rectangles keep partial panel updates cheap on battery.

F.05

Embedded + desktop

ESP32 through ESP-IDF, Windows through SDL3. Platform setup lives in its own package, so retargeting does not touch your code.

F.06

Packages as DLLs

Every package is a standalone shared library, never baked into the core. Ship with no rendering, no input, no audio — just the lifecycle.

F.07

Asset pipeline

JSON .scene files and .tex textures, keyed by GUID, exported to MessagePack for the device. AssetRef<T> fields resolve to live pointers during Awake.

F.08

Tweens &amp; easings

31 easings across Sine, Quad, Cubic, Quart, Quint, Expo, Circ, Back, Elastic and Bounce — as a TweenComponent or straight from TweenManager.

F.09

In-editor profiler

Hierarchy, flat and flame-graph views, memory stats and frame capture to PNG, in the editor, with no external dependency.

04platforms

One codebase.
Tiny boards to big screens.

Swap the integration package to retarget. Display and input are optional too — leave them out for headless services and data loggers.

• shipping

ESP32

The primary embedded target. Integration is against ESP-IDF itself, so any variant the SDK supports is in scope. deki-esp32-integration and deki-lovyangfx-integration cover SD, memory, display and touch, and the editor pipeline runs through to hardware.

• shipping

Windows desktop

Window, input and filesystem via deki-sdl3-integration and deki-desktop-integration. This is what the editor itself runs on.

• experimental

Linux desktop

The engine, the editor and the same two integration packages build and run on GCC 16, verified end to end on a Steam Deck. Opening a project and saving a scene go through the system's own file chooser.

! NOTE Bring a hardware FPU. Transforms, tweens, particle modifiers and rendering all lean on native float. Soft-float runs everything, just much slower.
05hardware as components

Sensors in the scene tree.

The engine defines the interfaces (IDekiRTC, IDekiGPS, IDekiIMU, IDekiAudio). Chip-specific components implement them and drop into a scene like anything else, configured in the inspector.

S.01
DS3231
I²C · 0x68
Battery-backed real-time clock. Read and write wall-clock time from any behaviour.
S.02
u-blox NEO-6M
UART · NMEA
NMEA 0183 over UART. Lat, lon, altitude, speed and fix quality through IDekiGPS.
S.03
LSM6DS3
I²C
6-axis accelerometer and gyroscope, with the chip's hardware pedometer counter.
S.04
MAX98357
I²S · class-D
Class-D mono amplifier. Feed it a PCM source, get sound.
06next

Ready to build?

The engine and its packages are on GitHub under Apache 2.0 and usable today. The editor isn't packaged for release yet — leave an email and you'll hear the day it is.