Skip to content

Why projectMM?

If you have been following the light-controller space for a while, you may know WLED, the popular open-source LED firmware. projectMM descends from that lineage, but it is a clean-sheet rebuild !, not a fork. Here is the family tree:

WLEDWLED-MMStarLightMoonLightprojectMM

WLED
WLED
WLED
WLED-MM
StarLight
StarLight
MoonLight
MoonLight
projectMM
projectMM

This page covers:


What we learned from MoonLight

Memory and flash on classic ESP32

MoonLight builds on ESP32-SvelteKit, a polished framework that delivered a great UI and stateful services out of the box. On S3 and P4 boards with PSRAM and ample flash it shines, but on classic ESP32 boards (no PSRAM, 4 MB flash) the framework's footprint left little room for effects and no room for Over-The-Air updates. Keeping everything running on smaller boards was an ongoing balancing act.

Living with a rich upstream

Forking ESP32-SvelteKit gave MoonLight a huge head-start. The trade-off is that diverging from upstream takes ongoing effort: every local change was marked (// 🌙) to survive a rebase, and as of today MoonLight is 1,271 commits ahead of SvelteKit upstream. Some features (Ethernet, migrate to ESP Async WebServer) needed to wait for upstream to add them, or be carefully grafted in. For a project that moves as fast as MoonLight, that cadence became a constraint.

MoonLight UI screenshot on an ESP32-S3 board
MoonLight running on a browser

Testing was an afterthought

Automated test scripts were added to MoonLight, but they could only verify trivial cases. Testing complex interactions meant restructuring code that was never designed with testing in mind: moving methods out of classes, breaking encapsulation, creating new bugs in the process.

Effective single-maintainer open source

MoonLight is a community project in name but about 95 % of the work has been carried by one person. As more users report issues, keeping up becomes impossible, especially when an actively-developed system breaks things that worked the week before.


Motivation for the rebuild

The rebuild was motivated by hitting a wall that the previous codebase could not break through. A complete rebuild is also an opportunity for ideas that would be too difficult or too disruptive to retrofit: e.g. a load-bearing test suite, a Platform Abstraction Layer, full PC parity, AI agent guarding become straightforward when the architecture is designed for them from day one.

MoonLight already makes the case for why a project like this needs to exist. This page explains why we went one step further.

projectMM is a working name. When the time is right, the repository will move to github.com/MoonModules, the same home as WLED-MM and MoonLight.


What projectMM does differently

Unit and Live testing: Guardrails first

The test suite is the primary safety net for every line of code merged.

There are two complementary test types:

Unit tests run entirely on your PC. They compile the real C++ module code and exercise advanced (non trivial) tests directly: module management, state transitions, control wiring, pixel output values, boundary conditions. No hardware required, results in seconds.

Live tests run against real devices over the network. A Python script calls the REST API, creates modules, sets props, reads health reports back and verifies the results match expectations. No USB cable needed; the device just needs to be on the same network.

Current numbers: 219 test cases / 1 035 assertions, all green.

  • SineEffectModule - loop() output matches expected waveform at tick 5
  • DriverLayer - two EffectsLayers blend correctly
  • StatefulModule - loadState before setup() applies pending props after setup()
  • ModuleManager - hot-reload recreates module with same id, correct type

→ Full list: Unit Test Results

  • Build a Ripples pipeline from scratch over REST; verify checksum changes over 5 ticks
  • Add a second EffectsLayer and confirm both effects run simultaneously
  • Resize GridLayout depth then width; confirm the pipeline resizes without crashing
  • Verify infrastructure modules (WiFi, SystemInfo) are present; add any that are missing

→ Full status: Deploy Summary

Test run output showing 219/219 passed
Deploy summary

These tests are the guardrails for all future development. If a change breaks something, the test run flags it immediately, whoever (or whatever) introduced it. The codebase is, by design, hard to break silently.

Built to work with modern tooling

projectMM is designed to be read and extended by anyone: a new contributor, an experienced C++ developer, or a coding assistant. The clear module boundaries, the explicit JSON-driven wiring, and the test suite all serve that goal regardless of your tooling preferences.

About 99 % of projectMM's code and documentation was written by AI agents. Not because it is a novelty, but because AI agents are here and won't go away, they are changing the way software is built, and we want to understand that change from the inside rather than watch it from the outside.

The approach was inspired by FastLED and Zach Vorhies, the architect of agent-driven development there, taught us how to use it. Agents were given precise instructions grounded in human ideas and in everything that was learned building MoonLight: the decisions, the dead ends, the retrospectives.

We use agile concepts: releases, sprints within releases, retrospectives, and a backlog. The release pages are the record of what was built, why, and what was learned. They serve a dual purpose: readable development history for humans, and short-term memory for AI agents starting a new session. The module reference pages, architecture docs, and standards are the long-term memory: the stable knowledge base an agent reads before proposing a change.

The testing framework is the guardrail that makes this safe. An AI agent can be wrong confidently. The test suite does not care: it catches regressions immediately, whoever or whatever introduced them.

This builds on the AI principles first practiced in MoonLight (AI Guidelines): 4EP · Unit tests · Reversible · Attribution · Documentation and taken further here. MoonLight was where we learned the patterns; projectMM is where we went deep.

A fully automated device farm

The entire deployment lifecycle: build, flash, filesystem upload, WiFi injection, run check, live test and summary report, is scripted in Python and can be driven by a single command:

python3 deploy/all.py
Photo of the device farm: several ESP32 boards on a rack, USB cables connected
Test rack with ESP32 boards, status: build in progress

This makes it practical to maintain a rack of ESP32 boards of different types, all automatically tested on every release. Adding a new board is as simple as plugging in a USB cable and running deploy/devicelist.py. We started with a standard ESP32-dev, adding S3 automatically changed to codebase to support it. Adding P4 will be done soon and is a real test of the codebase adaptability.

→ Details: Deploy Guide

Runs on PC, not just ESP32

projectMM runs natively on macOS, Linux, and Windows. This has two immediate benefits:

  • Fast iteration. Develop and test an effect on your laptop, no flashing required.
  • PC as a network node. Once Art-Net out lands (Release 4), any effect running on projectMM (all MoonLight effects will be migrated) can drive Art-Net-compatible fixtures (off-the-shelf or other projectMM devices), over the network.

This is made possible by the Platform Abstraction Layer (PAL): a thin header-only namespace that wraps all platform calls (timing, memory, GPIO, UDP, filesystem, FreeRTOS) so that modules contain zero #ifdef. The right implementation is selected at compile time. Adding a new platform means updating PAL in one place, not hunting through module code.

Everything is a Module

MoonLight distinguished between Modules (menu items) and Nodes (effects, modifiers, layouts, drivers inside a module). In projectMM everything is a Module with the same setup() / loop() / teardown() lifecycle. The runtime does not know about lights, light control is just a set of modules. The same shape works for audio processing, sensor pipelines, or anything else that runs in a loop.

Module overview · Add a Module

A UI that writes itself

Screenshot of the projectMM UI showing module cards with auto-generated controls
Browser screenshot of the projectMM UI linking to test result pages

The frontend is plain HTML / CSS / JavaScript. No framework, no build step, no Svelte. Every Module describes its controls as JSON, and the frontend renders them automatically. Adding a new module requires zero frontend code. Despite its small footprint the UI is fully capable, and the clean module-driven approach means it keeps pace with new modules without any extra effort.

The real Moon Module

Both of these ideas: everything is a module, and a UI that writes itself, were first realised in StarLight. MoonLight made a deliberate trade: adopting SvelteKit brought a much more polished UI and solid stateful services, which was absolutely the right call at the time. In doing so the module concept was adapted into Modules and Nodes, and the self-describing UI shared space with Svelte's own rendering layer. Those were reasonable choices given the tooling and expertise available then.

MoonLight still improved dramatically compared to what came before: better structure, better tooling, a more powerful effects engine. projectMM takes that maturity and brings the original idea back in its purest form: a single, clean module abstraction with no framework in the way.

MoonModules logo
MoonModules logo

Now we finally understand what MoonModules was always pointing at. The Module in projectMM is the real Moon Module. 🌙

Multi-device as a first-class goal

The test infrastructure makes multi-device scenarios easy to validate automatically:

  • One device sending Art-Net (or DDP / E1.31), another receiving it, a live test can trigger the send, capture the receive, and verify the result.
  • SuperSync, a long-promised feature first demoed on WLED-MM: devices discovering each other, one controller distributing an effect across a swarm of nodes. Think drone-swarm light shows, fully scripted and tested.
Art-Net running multiple layers
Art-Net running multiple layers

Where things stand today

Capability Status
Unit tests ✅ 219/219 passing
PC build + live tests
ESP32 build + live tests ✅ (esp32s3_n16r8)
Effects (Sine, Ripples, Lines, Brightness modifier)
Full Python deploy pipeline
Multi-device discovery ✅ Release 4 Sprint 1B
PSRAM pixel buffers ✅ Release 4 Sprint 2
Art-Net out 🔜 Release 4 Sprint 6
ESP32-P4 support 🔜 Release 4 Sprint 8
Windows support 🔜 Release 4 Sprint 9

→ Live status: Deploy Summary
→ Full roadmap: Development History


Want to explore further?