View on GitHub

arcade-image-bayermatrix

[Makecode arcade] bayer matrix to make pseudo opacity for image.

Lessons from arcade-polymesh

Why arcade-image-bayermatrix uses a register-based design instead of a class/OOP-based one.


Background: arcade-polymesh

Before this extension, Quarequin (then posting as Phatiski) maintained arcade-polymesh — a remix of a MakeCode Arcade 3D engine originally forked from Brohann3d’s work. Polymesh introduced a class-based mesh system to manage points, grids, and faces, which made the API feel familiar to anyone used to OOP-style 3D engines.

What went wrong (click to expand)
The class-based mesh abstraction grew hard to reason about as more AI-assisted contributions were layered onto it. Each new class, inheritance layer, and dynamically-constructed mesh object added surface area that a human reviewer had to trace through by hand. Eventually the extension was archived under the header **"End Of Extension"**, documented explicitly as a case study: AI-heavy, OOP-heavy extension code becomes **unmanageable, unoptimizable, and effectively unreviewable by a human maintainer**. The failure wasn't that AI was used — it was that AI was used to generate *control flow and structure*, the part that's hardest for a human to audit and easiest for a repo to quietly rot around.

The takeaway

Use AI for data, not for structure.

For arcade-image-bayermatrix, the Bayer matrix constants were sourced from LLM (Gemini) like ai-generated constant — a lookup table has exactly one correct answer for a given matrix size, so there’s nothing to “review” beyond checking the numbers. Every part of the extension that decides how a picture gets drawn — buffer layout, threshold comparisons, draw-image — was written and optimized by hand.

But polymesh’s failure wasn’t only about where the AI code came from — it was about the shape the code took once it existed: classes, instances, inheritance. So the second decision follows from the first: don’t just keep structure human-written, keep the structure itself simple enough that a human can review it in one pass.

Why register-based instead of class-based

This MakeCode Arcade extensions run in an environment with no OOP layer at the interpreter level, so a class-based mesh (like polymesh’s) is already fighting the runtime it sits on top of. register-based design takes the opposite approach:

Class-based (polymesh)register-based (bayermatrix)
- Objects/instances created to hold per-sprite or per-mesh state - State scattered across instance fields, inheritance chains - New allocations possible every frame or every mesh operation - Reviewing a change means tracing class hierarchy and method overrides - A fixed set of variables ("registers") declared **once**, at init - All draw calls read/write the same flat register set — no new objects - Zero allocation inside the render loop - Reviewing a change means checking register reads/writes in a flat, linear flow

This is the same idea as reserving registers/stack slots at the start of an assembly routine instead of allocating memory dynamically mid-execution: declare what you need up front, reuse it for the life of the program.

What this buys the project

  1. Stable FPS — no per-frame allocation means no GC-driven frame drops, which matters most on Arcade’s constrained hardware (SAMD51/STM32-class boards).
  2. No dynamic code generation — every code path is static, so there’s nothing an interpreter has to construct or JIT on the fly.
  3. A narrow, auditable surface — a reviewer only needs to check a flat set of register variables and how they’re mutated, not an inheritance tree. This directly avoids the “unmanageable, unoptimizable” outcome that ended arcade-polymesh.

Summary

  arcade-polymesh arcade-image-bayermatrix
AI’s role Generated structure and logic Supplied one reference data table (Bayer constants) only
State model Class instances, inheritance Flat registers, declared once at init
Runtime cost New allocations per mesh op Zero allocation in the render loop
Reviewability Degraded over time → archived Kept intentionally narrow from the start
Outcome Archived as a cautionary case study Built because of that case study