Skip to content

Conversation

@willmmiles
Copy link
Member

Pursuant to the ongoing discussions regarding handling large numbers of effects, this spike targets the core API changes I think set the most practical direction for future effect management. The design intent is to preserve compatibility with existing APIs (up to the 255 effect limit), and open a way for new APIs to break that limitation with minimal disruption to existing integrations.

First, the internal upgrade:

The key API changes center around supporting "non-contiguous" effect IDs -- be it names, a paging system, a usermod indexing scheme, or whatever. In all cases, the "effect identifier" is no longer strictly an index in to a vector. To this end, I refactored the effect metadata to a new struct Effect, keeping all per-effect information (name, function, id) in one place, and built an interface that relies on using functions to look up the Effect entry by name or ID.

Internal API changes:

  • New:
    • Adds new struct Effect to collect effect-related data in one place
    • Moves global list of effects from class WS2812FX to new namespace Effects
    • Add effect lookup functions Effects::getEffectById() or Effects::getEffectByName(). Function implementations can be replaced or extended to support different indexing approaches (larger id type? numeric ID maps?) without breaking API compatibility.
  • Changed:
    • Segments now use an Effect* to look up the function.
    • Mode-related API on WS2812FX now implemented by calling Effects namespace functions.
    • Segment::mode is preserved for API compatibility, but marked as deprecated.
  • Removed:
    • WS2812FX::getModeDataSrc() was the one existing internal API that could not be trivially supported. This impacted one usermod.

External API changes:
None. (Or at least none intentionally; I might have missed a special case re "Solid"'s metadata (or lack thereof)). Endpoints 'json/fxda' and 'json/eff' continue to return arrays where the index in to the array matches the 'getEffectById()' callback. Those arrays remain limited to 255 entries to ensure strict compatibility.


From there, the external JSON API is expanded:

  • New 'json/fxmap' endpoint returns a JSON object, where keys are the effect names, and the values objects of the form {"info": "...", "id": id}, where "id" is only present if the effect id is representable in a uint8_t. This allows an arbitrarily long list of effects to be described to a client without gaps for unused IDs.
  • JSON API now accepts {"ef": "effect name"} when deserializing a segment, and emits this key when serializing. If "ef" and "fx" are both supplied, "ef" has precedence.

The following commit adapts the web UI to use this API instead of ids.


The last commit saves some RAM by moving the Effect objects to code space.


Implementation notes:

  • I have made no effort to optimize effect name/id lookup. This does not matter to rendering as the Effect pointer is stored directly. This could be added later to speed up some of the API calls.
  • A strnchr_P function for ESP8266 would be nice.
    • or alternately, we could choose to store effect name and metadata in separate literals. That might be a breaking API change though - I don't know if it's possible to constexpr-transform the existing literals at compile time.
    • Another alternative might be store the name length (calculated at compile time).
  • Automatic "legacy" ID assignment is still performed for effects up to 255. After that either names will still work; and the architecture permits us to easily expand the numeric ID data type and/or add some expanded ID space as a new API (pages, a configurable mapper, or all of the above).
  • Functions to select a random effect, or "step through" effects, could be added to namespace Effects. Current implementations of these features in various endpoints work by mutating IDs, which will be problematic if the ID space becomes sparse.
  • The fxmap endpoint implementation either fits a whole effect in to a packet, or defers it for the next one. This is an inefficient use of bandwidth: keeping a local line buffer would allow it to be more bandwidth friendly at the cost of more RAM usage.
  • This implemention still keeps all Effect*s in a dynamically allocated vector. A future design could also offer a compile-time static list, saving a bit more RAM. The dynamic list could be kept (it's cost is small when empty) for compatibility with older APIs as well as providing a foundation to support runtime-defined effects (ARTI-FX or similar) as fully integrated members.

Define an "Effect" struct and move the global list out of WS2812FX.
The important change is that the id code is now a property of the Effect
instead of an index in to a vector.

We require retrieving an ID code for an effect to call through a
function.  This allows us to change the back end implementation of the
numeric ID map in the future.

Segment::mode is preserved, but marked as deprecated.

Usermods not updated yet.
getModeDataSrc() was the API we lost.
Add an endpoint that lists all FX as objects, without any gaps
Move the Effect objects for core effects in to static memory.  Saves a
little RAM.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 22, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant