# FastLED Core Library (`src/fl`)

This document introduces the FastLED core library housed under `src/fl/`, first by listing its headers, then by progressively expanding into an educational guide for two audiences:
- First‑time FastLED users who want to understand what lives below `FastLED.h` and how to use common utilities
- Experienced C++ developers exploring the `fl::` API as a cross‑platform, STL‑free foundation

FastLED avoids direct dependencies on the C++ standard library in embedded contexts and offers its own STL‑like building blocks in the `fl::` namespace.

## Table of Contents

- [Overview and Quick Start](#overview-and-quick-start)
- [Header Groups (5 major areas)](#header-groups-5-major-areas)
  - [STL‑like data structures and core utilities](#1-stl-like-data-structures-and-core-utilities)
  - [Graphics, geometry, and rendering](#2-graphics-geometry-and-rendering)
  - [Color, math, and signal processing](#3-color-math-and-signal-processing)
  - [Concurrency, async, and functional](#4-concurrency-async-and-functional)
  - [I/O, JSON, and text/formatting](#5-io-json-and-textformatting)
- [Comprehensive Module Breakdown](#comprehensive-module-breakdown)
- [Guidance for New Users](#guidance-for-new-users)
- [Guidance for C++ Developers](#guidance-for-c-developers)

---

## Overview and Quick Start

### What is `fl::`?

`fl::` is FastLED’s cross‑platform foundation layer. It provides containers, algorithms, memory utilities, math, graphics primitives, async/concurrency, I/O, and platform shims designed to work consistently across embedded targets and host builds. It replaces common `std::` facilities with equivalents tailored for embedded constraints and portability.

Key properties:
- Cross‑platform, embedded‑friendly primitives
- Minimal dynamic allocation where possible; clear ownership semantics
- Consistent naming and behavior across compilers/toolchains
- Prefer composable, header‑driven utilities

### Goals and design constraints

- Avoid fragile dependencies on `std::` in embedded builds; prefer `fl::` types
- Emphasize deterministic behavior and low overhead
- Provide familiar concepts (vector, span, optional, variant, function) with embedded‑aware implementations
- Offer safe RAII ownership types and moveable wrappers for resource management
- Keep APIs flexible by preferring non‑owning views (`fl::span`) as function parameters

### Naming and idioms

- Names live in the `fl::` namespace
- Prefer `fl::span<T>` as input parameters over owning containers
- Use `fl::shared_ptr<T>` / `fl::unique_ptr<T>` instead of raw pointers
- Favor explicit ownership and lifetimes; avoid manual `new`/`delete`

### Getting started: common building blocks

Include the top‑level header you need, or just include `FastLED.h` in sketches. When writing platform‑agnostic C++ within this repo, include the specific `fl/` headers you use.

Common types to reach for:
- Containers and views: `fl::vector<T>`, `fl::deque<T>`, `fl::span<T>`, `fl::slice<T>`
- Strings and streams: `fl::string`, `fl::ostream`, `fl::sstream`, `fl::printf`
- Optionals and variants: `fl::optional<T>`, `fl::variant<...>`
- Memory/ownership: `fl::unique_ptr<T>`, `fl::shared_ptr<T>`, `fl::weak_ptr<T>`
- Functional: `fl::function<Signature>` (in `fl/stl/function.h`), `fl::function_list<Signature>` (in `fl/stl/function.h`)
- Concurrency: `fl::thread`, `fl::mutex`, `fl::thread_local`
- Async: `fl::promise<T>`, `fl::task`
- Math: `fl::math`, `fl::sin32`, `fl::random`, `fl::gamma`, `fl::gradient`
- Graphics: `fl::raster`, `fl::screenmap`, `fl::rectangular_draw_buffer`, `fl::downscale`, `fl::supersample`
- Color: `fl::hsv`, `fl::hsv16`, `fl::colorutils`
- JSON: `fl::json` with safe defaults and ergonomic access

Example: using containers, views, and ownership

```cpp
#include "fl/stl/vector.h"
#include "fl/stl/span.h"
#include "fl/stl/memory.h"   // for fl::make_unique

void process(fl::span<const int> values) {
    // Non-owning view over contiguous data
}

void example() {
    fl::vector<int> data;
    data.push_back(1);
    data.push_back(2);
    data.push_back(3);

    process(fl::span<const int>(data));

    auto ptr = fl::make_unique<int>(42);
    // Exclusive ownership; automatically freed when leaving scope
}
```

Example: JSON with safe default access

```cpp
#include "fl/stl/json.h"

void json_example(const fl::string& jsonStr) {
    fl::json json = fl::json::parse(jsonStr);
    int brightness = json["config"]["brightness"] | 128;  // default if missing
    bool enabled = json["enabled"] | false;
}
```

---

## Header Groups (5 major areas)

### 1) STL‑like data structures and core utilities

Containers, views, algorithms, compile‑time utilities, memory/ownership, portability helpers.

- Containers: `vector.h`, `deque.h`, `queue.h`, `priority_queue.h`, `set.h`, `map.h`, `unordered_set.h`, `hash_map.h`, `hash_set.h`, `rbtree.h`, `stl/bitset.h`, `stl/bitset_dynamic.h`
- Views and ranges: `span.h`, `slice.h`, `range_access.h`
- Tuples and algebraic types: `tuple.h`, `pair.h`, `optional.h`, `variant.h`
- Algorithms and helpers: `algorithm.h`, `transform.h`, `comparators.h`, `range_access.h`
- SIMD and optimization primitives: `stl/simd.h`
- Types and traits: `types.h`, `type_traits.h`, `initializer_list.h`, `utility.h`, `move.h`, `template_magic.h`, `stdint.h`, `cstddef.h`
- Memory/ownership: `unique_ptr.h`, `shared_ptr.h`, `weak_ptr.h`, `scoped_ptr.h`, `scoped_array.h`, `referent.h`, `allocator.h`, `memory.h`, `memfill.h`, `inplacenew.h`
- Portability and compiler control: `compiler_control.h`, `force_inline.h`, `virtual_if_not_avr.h`, `has_define.h`, `register.h`, `warn.h`, `trace.h`, `dbg.h`, `log.h`, `assert.h`, `unused.h`, `export.h`, `dll.h`, `deprecated.h`, `avr_disallowed.h`, `bit_cast.h`, `id_tracker.h`, `insert_result.h`, `singleton.h`

Per‑header quick descriptions:

- `vector.h`: Dynamically sized contiguous container with embedded‑friendly API.
- `deque.h`: Double‑ended queue for efficient front/back operations.
- `queue.h`: FIFO adapter providing push/pop semantics over an underlying container.
- `priority_queue.h`: Heap‑based ordered queue for highest‑priority retrieval.
- `set.h`: Ordered unique collection with deterministic iteration.
- `map.h`: Ordered key‑value associative container.
- `unordered_set.h`: Hash‑based unique set for average O(1) lookups.
- `hash_map.h`: Hash‑based key‑value container tuned for embedded use.
- `hash_set.h`: Hash set implementation complementing `hash_map.h`.
- `rbtree.h`: Balanced tree primitive used by ordered containers.
- `stl/bitset.h`: Fixed‑size compile‑time bitset operations.
- `stl/bitset_dynamic.h`: Runtime‑sized bitset for flexible masks.
- `span.h`: Non‑owning view over contiguous memory (preferred function parameter).
- `slice.h`: Strided or sub‑range view utilities for buffers.
- `range_access.h`: Helpers to unify begin/end access over custom ranges.
- `tuple.h`: Heterogeneous fixed‑size aggregate with structured access.
- `pair.h`: Two‑value aggregate type for simple key/value or coordinate pairs.
- `optional.h`: Presence/absence wrapper to avoid sentinel values.
- `variant.h`: Type‑safe tagged union for sum types without heap allocation.
- `algorithm.h`: Core algorithms (search, sort helpers, transforms) adapted to `fl::` containers.
- `transform.h`: Functional style element‑wise transformations with spans/ranges.
- `comparators.h`: Reusable comparator utilities for ordering operations.
- `stl/simd.h`: SIMD-accelerated register operations for cache-friendly pipelines (u8x16, u32x4, f32x4 vectors with platform delegation).
- `types.h`: Canonical type aliases and shared type definitions.
- `type_traits.h`: Compile‑time type inspection and enable_if‑style utilities.
- `initializer_list.h`: Lightweight initializer list support for container construction.
- `utility.h`: Miscellaneous helpers (swap, forward, etc.) suitable for embedded builds.
- `move.h`: Move/forward utilities mirroring standard semantics.
- `template_magic.h`: Metaprogramming helpers to simplify template code.
- `stdint.h`: Fixed‑width integer definitions for cross‑compiler consistency.
- `cstddef.h`: Size/ptrdiff and nullptr utilities for portability.
- `unique_ptr.h`: Exclusive ownership smart pointer with RAII semantics.
- `shared_ptr.h`: Reference‑counted shared ownership smart pointer.
- `weak_ptr.h`: Non‑owning reference to `shared_ptr`‑managed objects.
- `scoped_ptr.h`: Scope‑bound ownership (no move) for simple RAII cleanup.
- `scoped_array.h`: RAII wrapper for array allocations.
- `referent.h`: Base support for referent/observer relationships.
- `allocator.h`: Custom allocators tailored for embedded constraints.
- `memory.h`: Low‑level memory helpers (construct/destroy, address utilities).
- `memfill.h`: Zero‑cost fill utilities (prefer over `memset` in codebase).
- `inplacenew.h`: Placement new helpers for manual lifetime management.
- `compiler_control.h`: Unified compiler warning/pragma control macros.
- `force_inline.h`: Portable always‑inline control macros.
- `virtual_if_not_avr.h`: Virtual specifier abstraction for AVR compatibility.
- `has_define.h`: Preprocessor feature checks and conditional compilation helpers.
- `register.h`: Register annotation shims for portability.
- `warn.h`: Always‑on warnings for issues detected at runtime (respects memory constraints).
- `trace.h`: Platform tracing utilities for debugging and performance analysis.
- `dbg.h`: General‑purpose debug output, controllable at compile-time via `FASTLED_FORCE_DBG`.
- `log.h`: Category‑based runtime logging system for selective subsystem diagnostics (SPI, RMT, VIDEO, I2S, LCD, UART, TIMING) with zero overhead when disabled.
- `assert.h`: Assertions suited for embedded/testing builds.
- `unused.h`: Intentional unused variable/function annotations.
- `export.h`/`dll.h`: Visibility/export macros for shared library boundaries.
- `deprecated.h`: Cross‑compiler deprecation annotations.
- `avr_disallowed.h`: Guardrails to prevent unsupported usage on AVR.
- `bit_cast.h`: Safe bit reinterpretation where supported, with fallbacks.
- `id_tracker.h`: ID generation/tracking utility for object registries.
- `insert_result.h`: Standardized result type for associative container inserts.
- `singleton.h`: Simple singleton helper for cases requiring global access.

### 2) Graphics, geometry, and rendering

Rasterization, coordinate mappings, paths, grids, resampling, draw buffers, and related glue.

- Raster and buffers: `raster.h`, `raster_sparse.h`, `rectangular_draw_buffer.h`
- Screen and tiles: `screenmap.h`, `tile2x2.h`
- Coordinates and mappings: `xmap.h`, `xymap.h`, `screenmap.h`
- Paths and traversal: `xypath.h`, `xypath_impls.h`, `xypath_renderer.h`, `traverse_grid.h`, `grid.h`, `line_simplification.h`
- Geometry primitives: `geometry.h`, `point.h`
- Resampling/scaling: `downscale.h`, `upscale.h`, `supersample.h`
- Glue and UI: `leds.h`, `ui.h`, `ui_impl.h`, `rectangular_draw_buffer.h`
- Specialized: `corkscrew.h`, `wave_simulation.h`, `wave_simulation_real.h`, `tile2x2.h`, `screenmap.h`

Per‑header quick descriptions:

- `raster.h`: Core raster interface and operations for pixel buffers.
- `raster_sparse.h`: Sparse/partial raster representation for memory‑efficient updates.
- `rectangular_draw_buffer.h`: Double‑buffered rectangular draw surface helpers.
- `screenmap.h`: Maps logical coordinates to physical LED indices/layouts.
- `tile2x2.h`: Simple 2×2 tiling utilities for composing larger surfaces.
- `xmap.h`: General coordinate mapping utilities.
- `xymap.h`: XY coordinate to index mapping helpers for matrices and panels.
- `xypath.h`: Path representation in XY space for drawing and effects.
- `xypath_impls.h`: Implementations and algorithms supporting `xypath.h`.
- `xypath_renderer.h`: Renders paths into rasters with configurable styles.
- `traverse_grid.h`: Grid traversal algorithms for curves/lines/fills.
- `grid.h`: Grid data structure and iteration helpers.
- `line_simplification.h`: Path simplification (e.g., Douglas‑Peucker‑style) for fewer segments.
- `geometry.h`: Basic geometric computations (distances, intersections, etc.).
- `point.h`: Small coordinate/vector primitive type.
- `downscale.h`: Resampling utilities to reduce resolution while preserving features.
- `upscale.h`: Upsampling utilities for enlarging frames.
- `supersample.h`: Anti‑aliasing via multi‑sample accumulation.
- `leds.h`: Integration helpers bridging LED buffers to rendering utilities.
- `ui.h` / `ui_impl.h`: Minimal UI adapter hooks for demos/tests.
- `corkscrew.h`: Experimental path/trajectory utilities for visual effects.
- `wave_simulation.h` / `wave_simulation_real.h`: Simulated wave dynamics for organic effects.

### 3) Color, math, and signal processing

Color models, gradients, gamma, math helpers, random, noise, mapping, and basic DSP.

- Color and palettes: `colorutils.h`, `colorutils_misc.h`, `hsv.h`, `hsv16.h`, `gradient.h`, `fill.h`, `five_bit_hd_gamma.h`, `gamma.h`
- Math and mapping: `math.h`, `math_macros.h`, `sin32.h`, `map_range.h`, `random.h`, `lut.h`, `clamp.h`, `clear.h`, `splat.h`, `transform.h`
- Noise and waves: `noise_woryley.h`, `wave_simulation.h`, `wave_simulation_real.h`
- DSP and audio: `fft.h`, `fft_impl.h`, `audio.h`, `audio_reactive.h`
- Time utilities: `time.h`, `time_alpha.h`

Per‑header quick descriptions:

- `colorutils.h`: High‑level color operations (blend, scale, lerp) for LED pixels.
- `colorutils_misc.h`: Additional helpers and niche color operations.
- `hsv.h` / `hsv16.h`: HSV color types and conversions (8‑bit and 16‑bit variants).
- `gradient.h`: Gradient construction, sampling, and palette utilities.
- `fill.h`: Efficient buffer/palette filling operations for pixel arrays.
- `five_bit_hd_gamma.h`: Gamma correction tables tuned for high‑definition 5‑bit channels.
- `gamma.h`: Gamma correction functions and LUT helpers.
- `math.h` / `math_macros.h`: Core math primitives/macros for consistent numerics.
- `sin32.h`: Fast fixed‑point sine approximations for animations.
- `map_range.h`: Linear mapping and clamping between numeric ranges.
- `random.h`: Pseudorandom utilities for effects and dithering.
- `lut.h`: Lookup table helpers for precomputed transforms.
- `clamp.h`: Bounds enforcement for numeric types.
- `clear.h`: Clear/zero helpers for buffers with type awareness.
- `splat.h`: Vectorized repeat/write helpers for bulk operations.
- `transform.h`: Element transforms (listed here as it is often used for pixel ops too).
- `noise_woryley.h`: Worley/cellular noise generation utilities.
- `wave_simulation*.h`: Wavefield simulation (also referenced in graphics).
- `fft.h` / `fft_impl.h`: Fast Fourier Transform interfaces and backends.
- `audio.h`: Audio input/stream abstractions for host/platforms that support it.
- `audio_reactive.h`: Utilities to drive visuals from audio features.
- `time.h`: Timekeeping helpers (millis/micros abstractions when available).
- `time_alpha.h`: Smoothed/exponential time‑based interpolation helpers.

### 4) Concurrency, async, and functional

Threads, synchronization, async primitives, eventing, and callable utilities.

- Threads and sync: `thread.h`, `mutex.h`, `thread_local.h`
- Async primitives: `promise.h`, `promise_result.h`, `task.h`, `async.h`
- Functional: `function.h` (in `fl/stl/`), `functional.h`
- Events and engine hooks: `engine_events.h`
- Interrupt service routines: `isr.h`

Per‑header quick descriptions:

- `thread.h`: Portable threading abstraction for supported hosts.
- `mutex.h`: Mutual exclusion primitive compatible with `fl::thread`. Almost all platforms these are fake implementations.
- `thread_local.h`: Thread‑local storage shim for supported compilers.
- `promise.h`: Moveable wrapper around asynchronous result delivery.
- `promise_result.h`: Result type accompanying promises/futures.
- `task.h`: Lightweight async task primitive for orchestration.
- `async.h`: Helpers for async composition and coordination.
- `fl/stl/function.h`: Type‑erased callable wrapper analogous to `std::function`, and `function_list` for multicast callbacks.
- `functional.h`: Adapters, binders, and predicates for composing callables.
- `engine_events.h`: Event channel definitions for engine‑style systems.
- `isr.h`: Cross-platform interrupt service routine (ISR) attachment API for timer and GPIO interrupts.

### 5) I/O, JSON, and text/formatting

Streams, strings, formatted output, bytestreams, filesystem, JSON.

- Text and streams: `string.h`, `str.h`, `ostream.h`, `istream.h`, `sstream.h`, `strstream.h`, `printf.h`
- JSON: `json.h`
- Bytestreams and I/O: `bytestream.h`, `bytestreammemory.h`, `io.h`, `file_system.h`, `fetch.h`

Per‑header quick descriptions:

- `string.h` / `str.h`: String types and helpers without pulling in `std::string`.
- `ostream.h` / `istream.h`: Output/input stream interfaces for host builds.
- `sstream.h` / `strstream.h`: String‑backed stream buffers and helpers.
- `printf.h`: Small, portable formatted print utilities.
- `json.h`: Safe, ergonomic `fl::json` API with defaulting operator (`|`).
- `bytestream.h`: Sequential byte I/O abstraction for buffers/streams.
- `bytestreammemory.h`: In‑memory byte stream implementation.
- `io.h`: General I/O helpers for files/streams where available.
- `file_system.h`: Minimal filesystem adapter for host platforms.
- `fetch.h`: Basic fetch/request helpers for network‑capable hosts.
## Comprehensive Module Breakdown

This section groups headers by domain, explains their role, and shows minimal usage snippets. Names shown are representative; see the header list above for the full inventory.

### Containers and Views

- Sequence and associative containers: `vector.h`, `deque.h`, `queue.h`, `priority_queue.h`, `set.h`, `map.h`, `unordered_set.h`, `hash_map.h`, `hash_set.h`, `rbtree.h`
- Non‑owning and slicing: `span.h`, `slice.h`, `range_access.h`

Why: Embedded‑aware containers with predictable behavior across platforms. Prefer passing `fl::span<T>` to functions.

```cpp
#include "fl/stl/vector.h"
#include "fl/stl/span.h"

size_t count_nonzero(fl::span<const uint8_t> bytes) {
    size_t count = 0;
    for (uint8_t b : bytes) { if (b != 0) { ++count; } }
    return count;
}
```

### Strings and Streams

- Text types and streaming: `string.h`, `str.h`, `ostream.h`, `istream.h`, `sstream.h`, `strstream.h`, `printf.h`

Why: Consistent string/stream facilities without pulling in the standard streams.

```cpp
#include "fl/string.h"
#include "fl/sstream.h"

fl::string greet(const fl::string& name) {
    fl::sstream ss;
    ss << "Hello, " << name << "!";
    return ss.str();
}
```

### Memory and Ownership

- Smart pointers and utilities: `unique_ptr.h`, `shared_ptr.h`, `weak_ptr.h`, `scoped_ptr.h`, `scoped_array.h`, `allocator.h`, `memory.h`, `memfill.h`

Why: RAII ownership with explicit semantics. Prefer `fl::make_shared<T>()`/`fl::make_unique<T>()` patterns where available, or direct constructors provided by these headers.

```cpp
#include "../fl/stl/shared_ptr.h"

struct Widget { int value; };

void ownership_example() {
    fl::shared_ptr<Widget> w(new Widget{123});
    auto w2 = w; // shared ownership
}
```

### Functional Utilities

- Callables and lists: `fl/stl/function.h`, `functional.h`

Why: Store callbacks and multicast them safely.

```cpp
#include "fl/stl/function.h"

void on_event(int code) { /* ... */ }

void register_handlers() {
    fl::function_list<void(int)> handlers;
    handlers.add(on_event);
    handlers(200); // invoke all
}
```

### Concurrency and Async

- Threads and synchronization: `thread.h`, `mutex.h`, `thread_local.h`
- Async primitives: `promise.h`, `promise_result.h`, `task.h`
- Interrupt service routines: `isr.h`

Why: Lightweight foundations for parallel work or async orchestration where supported.

```cpp
#include "fl/promise.h"

fl::promise<int> compute_async(); // returns a moveable wrapper around a future-like result
```

### Interrupt Service Routines (ISR)

- Cross-platform ISR API: `isr.h`

Why: Unified interrupt handling across ESP32, Teensy, AVR, STM32, and other platforms. Provides timer-based and GPIO-based interrupt attachment with consistent configuration and lifecycle management.

**Key features:**
- Timer-based interrupts with configurable frequencies (platform-dependent range)
- GPIO-based external interrupts with edge/level triggering
- Priority control and platform-specific flags
- Enable/disable without detachment
- User data context passing
- Platform capabilities query (frequency ranges, priority levels)

**Platform support:**
- ESP32 (Xtensa/RISC-V): Hardware timer groups, 1 Hz - 80 MHz, priorities 1-7
- Teensy: IntervalTimer, up to ~150 kHz
- AVR: Timer1, frequency via prescaler
- STM32: Hardware timers with NVIC priorities
- Stub: Software simulation for testing

Basic timer interrupt example:

```cpp
#include "fl/stl/isr.h"

// Counter updated from ISR (use atomic or volatile for shared state)
static volatile uint32_t tick_count = 0;

// ISR handler - must be fast and IRAM-safe on ESP32
static void my_timer_isr(void* user_data) {
    tick_count++;
}

void setup() {
    // Configure a 1 kHz (1000 Hz) timer interrupt
    fl::isr::config config;
    config.handler = my_timer_isr;
    config.user_data = nullptr;
    config.frequency_hz = 1000;  // 1 kHz
    config.priority = fl::isr::ISR_PRIORITY_MEDIUM;
    config.flags = fl::isr::ISR_FLAG_IRAM_SAFE;  // Required for ESP32

    fl::isr::handle handle;
    int result = fl::isr::attach_timer_handler(config, &handle);

    if (result == 0) {
        // ISR is now active
        FL_DBG("Timer ISR attached successfully");
    } else {
        FL_WARN("Failed to attach ISR: " << fl::isr::get_error_string(result));
    }
}
```

Advanced example with user data and enable/disable:

```cpp
#include "fl/stl/isr.h"

struct ISRContext {
    volatile uint32_t count;
    volatile uint32_t max_count;
};

static void counting_isr(void* user_data) {
    ISRContext* ctx = static_cast<ISRContext*>(user_data);
    if (ctx->count < ctx->max_count) {
        ctx->count++;
    }
}

void example_with_control() {
    ISRContext ctx = { 0, 1000 };

    // Configure 10 kHz timer with user context
    fl::isr::config config;
    config.handler = counting_isr;
    config.user_data = &ctx;
    config.frequency_hz = 10000;  // 10 kHz
    config.priority = fl::isr::ISR_PRIORITY_HIGH;
    config.flags = fl::isr::ISR_FLAG_IRAM_SAFE;

    fl::isr::handle handle;
    if (fl::isr::attach_timer_handler(config, &handle) == 0) {
        // Run for a while
        delay(100);

        // Temporarily disable ISR
        fl::isr::disable_handler(handle);
        FL_DBG("ISR disabled, count: " << ctx.count);

        delay(100);

        // Re-enable ISR
        fl::isr::enable_handler(handle);
        FL_DBG("ISR re-enabled");

        delay(100);

        // Clean up when done
        fl::isr::detach_handler(handle);
        FL_DBG("Final count: " << ctx.count);
    }
}
```

GPIO interrupt example:

```cpp
#include "fl/stl/isr.h"

static void button_isr(void* user_data) {
    // Handle button press (keep this fast!)
    FL_DBG("Button pressed!");
}

void setup_gpio_interrupt() {
    fl::isr::config config;
    config.handler = button_isr;
    config.user_data = nullptr;
    config.priority = fl::isr::ISR_PRIORITY_MEDIUM;
    config.flags = fl::isr::ISR_FLAG_EDGE_RISING;  // Trigger on rising edge

    fl::isr::handle handle;
    uint8_t button_pin = 2;  // GPIO pin number

    if (fl::isr::attach_external_handler(button_pin, config, &handle) == 0) {
        FL_DBG("Button ISR attached to pin " << button_pin);
    }
}
```

Platform capability queries:

```cpp
#include "fl/stl/isr.h"

void print_platform_info() {
    FL_DBG("Platform: " << fl::isr::get_platform_name());
    FL_DBG("Max timer frequency: " << fl::isr::get_max_timer_frequency() << " Hz");
    FL_DBG("Min timer frequency: " << fl::isr::get_min_timer_frequency() << " Hz");
    FL_DBG("Max priority: " << fl::isr::get_max_priority());

    if (fl::isr::requires_assembly_handler(fl::isr::ISR_PRIORITY_MAX)) {
        FL_DBG("Maximum priority requires assembly handler");
    }
}
```

**Important ISR handler requirements:**
- Must execute quickly (typically <10μs)
- No blocking operations (no delay, sleep, or waiting for locks)
- No dynamic memory allocation (malloc/free)
- On ESP32: mark as IRAM-safe and use `ISR_FLAG_IRAM_SAFE` flag
- Use atomic types or proper synchronization for shared state
- Platform-specific restrictions may apply (see `fl/isr.h` for details)

**Priority guidelines:**
- `ISR_PRIORITY_LOW` (1): Non-critical background tasks
- `ISR_PRIORITY_MEDIUM` (2): Normal LED timing operations
- `ISR_PRIORITY_HIGH` (3): Critical timing-sensitive operations (recommended max)
- `ISR_PRIORITY_CRITICAL` (4+): Experimental, may require assembly on some platforms

See `src/fl/isr.h` for complete API documentation and platform-specific configuration flags.

### Category-Based Logging

- Category logging system: `log.h`, integrated via `dbg.h`

**What it is:** A compile-time selective logging system that enables detailed diagnostics for specific subsystems (SPI, RMT, I2S, GPIO, PIO, DMA, TIMER, and many more) with **zero overhead when disabled**. Unlike `FL_DBG` (general-purpose), category logging provides structured, category-specific diagnostics by enabling only the categories you need.

**Key features:**
- **Compile-time control**: Enable/disable categories via preprocessor defines (`-DFASTLED_LOG_<CATEGORY>_ENABLED`)
- **Zero overhead disabled**: Disabled categories compile to nothing (~0 bytes, ~0 cycles)
- **Stream formatting**: Same syntax as `FL_WARN`: `FL_LOG_SPI("msg: " << value)`
- **No runtime state**: Clean, stateless implementation with zero memory overhead
- **Platform-aware**: Uses `FL_WARN` internally, respects platform constraints and memory limits

**Available categories:**

Hardware interfaces:
- `FL_LOG_SPI` - Serial Peripheral Interface operations
- `FL_LOG_RMT` - ESP32 RMT peripheral
- `FL_LOG_PARLIO` - ESP32-P4 parallel I/O
- `FL_LOG_FLEXIO` - Teensy 4.x FlexIO peripheral
- `FL_LOG_OBJECTFLED` - Teensy 4.x ObjectFLED DMA


**Comparison with alternatives:**

| Feature | `FL_DBG` | `FL_WARN` | `FL_LOG_*` |
|---------|----------|-----------|-----------|
| Purpose | General debug | Critical warnings | Category diagnostics |
| Selective enable | No (all-or-nothing) | No | Yes (per-category) |
| Default state | Memory-dependent | Always enabled | Always disabled |
| Memory | ~5KB | Variable | 0 bytes |
| Control mechanism | Compile-time | N/A | Compile-time only |

**How to enable logging:**

Add to `platformio.ini` or Arduino IDE build flags:

```ini
# Enable SPI logging code
build_flags = -DFASTLED_LOG_SPI_ENABLED

# Enable multiple categories
build_flags =
    -DFASTLED_LOG_SPI_ENABLED
    -DFASTLED_LOG_I2S_ENABLED
    -DFASTLED_LOG_TIMER_ENABLED

# Enable multiple categories (alternative syntax)
build_flags =
    -DFASTLED_LOG_SPI_ENABLED
    -DFASTLED_LOG_RMT_ENABLED
    -DFASTLED_LOG_VIDEO_ENABLED
```

**Logging in your code:**

```cpp
#include "fl/log.h"

void myCustomDriver::initialize(uint32_t freq_hz) {
    FL_LOG_I2S("I2S initialized");
    FL_LOG_I2S("  Frequency: " << freq_hz << " Hz");
    FL_LOG_I2S("  Buffer size: " << mBufferSize << " bytes");
}

void myCustomDriver::transmit(fl::span<const uint8_t> data) {
    FL_LOG_I2S("Transmitting " << data.size() << " bytes");
}
```

**Implementation details:**
- Pure compile-time control via preprocessor
- Zero-overhead disabled logs: Compile to `FL_DBG_NO_OP(...)` → eliminated by optimizer
- Enabled logs: Direct output to `FL_WARN`
- No runtime state, no memory overhead, no performance cost
- Users can implement their own runtime control if needed via wrapper functions

### Math, Random, and DSP

- Core math and helpers: `math.h`, `math_macros.h`, `sin32.h`, `random.h`, `map_range.h`
- Color math: `gamma.h`, `gradient.h`, `colorutils.h`, `colorutils_misc.h`, `hsv.h`, `hsv16.h`, `fill.h`
- FFT and analysis: `fft.h`, `fft_impl.h`

Why: Efficient numeric operations for LED effects, audio reactivity, and transforms.

```cpp
#include "fl/gamma.h"

uint8_t apply_gamma(uint8_t v) {
    return fl::gamma::correct8(v);
}
```

### Geometry and Grids

- Basic geometry: `point.h`, `geometry.h`
- Grid traversal and simplification: `grid.h`, `traverse_grid.h`, `line_simplification.h`

Why: Building blocks for 2D/3D layouts and path operations.

### Graphics, Rasterization, and Resampling

- Raster and buffers: `raster.h`, `raster_sparse.h`, `rectangular_draw_buffer.h`
- Screen mapping and tiling: `screenmap.h`, `tile2x2.h`, `xmap.h`, `xymap.h`
- Paths and renderers: `xypath.h`, `xypath_impls.h`, `xypath_renderer.h`, `traverse_grid.h`
- Resampling and effects: `downscale.h`, `upscale.h`, `supersample.h`

Why: Efficient frame manipulation for LED matrices and coordinate spaces.

```cpp
#include "fl/downscale.h"

// Downscale a high‑res buffer to a target raster (API varies by adapter)
```

#### Graphics Deep Dive

This section explains how the major graphics utilities fit together and how to use them effectively for high‑quality, high‑performance rendering on LED strips, matrices, and complex shapes.

- **Wave simulation (1D/2D)**
  - Headers: `wave_simulation.h`, `wave_simulation_real.h`
  - Concepts:
    - Super‑sampling for quality: choose `SuperSample` factors to run an internal high‑resolution simulation and downsample for display.
    - Consistent speed at higher quality: call `setExtraFrames(u8)` to update the simulation multiple times per frame to maintain perceived speed when super‑sampling.
    - Output accessors: `getf`, `geti16`, `getu8` return float/fixed/byte values; 2D version offers cylindrical wrapping via `setXCylindrical(true)`.
  - Tips for “faster” updates:
    - Use `setExtraFrames()` to advance multiple internal steps per visual frame without changing your outer timing.
    - Prefer `getu8(...)` when feeding color functions or gradients on constrained devices.

- **fl::Leds – array and mapped matrix**
  - Header: `leds.h`; mapping: `xymap.h`
  - `fl::Leds` wraps a `CRGB*` so you can treat it as:
    - A plain `CRGB*` via implicit conversion for classic FastLED APIs.
    - A 2D surface via `operator()(x, y)` that respects an `XYMap` (serpentine, line‑by‑line, LUT, or custom function).
  - Construction:
    - `Leds(CRGB* leds, u16 width, u16 height)` for quick serpentine/rectangular usage.
    - `Leds(CRGB* leds, const XYMap& xymap)` for full control (serpentine, rectangular, user function, or LUT).
  - Access and safety:
    - `at(x, y)`/`operator()(x, y)` map to the correct LED index; out‑of‑bounds is safe and returns a sentinel.
    - `operator[]` exposes row‑major access when the map is serpentine or line‑by‑line.

- **Matrix mapping (XYMap)**
  - Header: `xymap.h`
  - Create maps for common layouts:
    - `XYMap::constructSerpentine(w, h)` for typical pre‑wired panels.
    - `XYMap::constructRectangularGrid(w, h)` for row‑major matrices.
    - `XYMap::constructWithUserFunction(w, h, XYFunction)` for custom wiring.
    - `XYMap::constructWithLookUpTable(...)` for arbitrary wiring via LUT.
  - Utilities:
    - `mapToIndex(x, y)` maps coordinates to strip index.
    - `has(x, y)` tests bounds; `toScreenMap()` converts to a float UI mapping.

- **XY paths and path rendering (xypath)**
  - Headers: `xypath.h`, `xypath_impls.h`, `xypath_renderer.h`, helpers in `tile2x2.h`, `transform.h`.
  - Purpose: parameterized paths in 
    \([0,1] \to (x,y)\) for drawing lines/curves/shapes with subpixel precision.
  - Ready‑made paths: point, line, circle, heart, Archimedean spiral, rose curves, phyllotaxis, Gielis superformula, and Catmull‑Rom splines with editable control points.
  - Rendering:
    - Subpixel sampling via `Tile2x2_u8` enables high quality on low‑res matrices.
    - Use `XYPath::drawColor` or `drawGradient` to rasterize into an `fl::Leds` surface.
    - `XYPath::rasterize` writes into a sparse raster for advanced composition.
  - Transforms and bounds:
    - `setDrawBounds(w, h)` and `setTransform(TransformFloat)` control framing and animation transforms.

- **Subpixel “splat” rendering (Tile2x2)**
  - Header: `tile2x2.h`
  - Concept: represent a subpixel footprint as a 2×2 tile of coverage values (u8 alphas). When a subpixel position moves between LEDs, neighboring LEDs get proportional contributions.
  - Use cases:
    - `Tile2x2_u8::draw(color, xymap, out)` to composite with color per‑pixel.
    - Custom blending: `tile.draw(xymap, visitor)` to apply your own alpha/compositing.
    - Wrapped tiles: `Tile2x2_u8_wrap` supports cylindrical wrap with interpolation for continuous effects.

- **Downscale**
  - Header: `downscale.h`
  - Purpose: resample from a higher‑resolution buffer to a smaller target, preserving features.
  - APIs:
    - `downscale(src, srcXY, dst, dstXY)` general case.
    - `downscaleHalf(...)` optimized 2× reduction (square or mapped) used automatically when sizes match.

- **Upscale**
  - Header: `upscale.h`
  - Purpose: bilinear upsampling from a low‑res buffer to a larger target.
  - APIs:
    - `upscale(input, output, inW, inH, xyMap)` auto‑selects optimized paths.
    - `upscaleRectangular` and `upscaleRectangularPowerOf2` bypass XY mapping for straight row‑major layouts.
    - Float reference versions exist for validation.

- **Corkscrew (cylindrical projection)**
  - Header: `corkscrew.h`
  - Goal: draw into a rectangular buffer and project onto a tightly wrapped helical/cylindrical LED layout.
  - Inputs and sizing:
    - `CorkscrewInput{ totalTurns, numLeds, Gap, invert }` defines geometry; helper `calculateWidth()/calculateHeight()` provide rectangle dimensions for buffer allocation.
  - Mapping and iteration:
    - `Corkscrew::at_exact(i)` returns the exact position for LED i; `at_wrap(float)` returns a wrapped `Tile2x2_u8_wrap` footprint for subpixel‑accurate sampling.
    - Use `toScreenMap(diameter)` to produce a `ScreenMap` for UI overlays or browser visualization.
  - Rectangular buffer integration:
    - `getBuffer()/data()` provide a lazily‑initialized rectangle; `fillBuffer/clearBuffer` manage it.
    - `readFrom(source_grid, use_multi_sampling)` projects from a high‑def source grid to the corkscrew using multi‑sampling for quality.

  - Examples

    1) Manual per‑frame draw (push every frame)

    ```cpp
    #include <FastLED.h>
    #include "fl/gfx/corkscrew.h"
    #include "fl/grid.h"
    #include "fl/time.h"

    // Your physical LED buffer
    constexpr uint16_t NUM_LEDS = 144;
    CRGB leds[NUM_LEDS];

    // Define a corkscrew geometry (e.g., 19 turns tightly wrapped)
    fl::Corkscrew::Input input(/*totalTurns=*/19.0f, /*numLeds=*/NUM_LEDS);
    fl::Corkscrew cork(input);

    // A simple rectangular source grid to draw into (unwrapped cylinder)
    // Match the corkscrew's recommended rectangle
    const uint16_t W = cork.cylinder_width();
    const uint16_t H = cork.cylinder_height();
    fl::Grid<CRGB> rect(W, H);

    void draw_pattern(uint32_t t_ms) {
        // Example: time‑animated gradient on the unwrapped cylinder
        for (uint16_t y = 0; y < H; ++y) {
            for (uint16_t x = 0; x < W; ++x) {
                uint8_t hue = uint8_t((x * 255u) / (W ? W : 1)) + uint8_t((t_ms / 10) & 0xFF);
                rect(x, y) = CHSV(hue, 255, 255);
            }
        }
    }

    void project_to_strip() {
        // For each LED index i on the physical strip, sample the unwrapped
        // rectangle using the subpixel footprint from at_wrap(i)
        for (uint16_t i = 0; i < NUM_LEDS; ++i) {
            auto tile = cork.at_wrap(float(i));
            // tile.at(u,v) gives {absolute_pos, alpha}
            // Blend the 2x2 neighborhood from the rectangular buffer
            uint16_t r = 0, g = 0, b = 0, a_sum = 0;
            for (uint16_t vy = 0; vy < 2; ++vy) {
                for (uint16_t vx = 0; vx < 2; ++vx) {
                    auto entry = tile.at(vx, vy); // pair<vec2i16, u8>
                    const auto pos = entry.first; // absolute cylinder coords
                    const uint8_t a = entry.second; // alpha 0..255
                    if (pos.x >= 0 && pos.x < int(W) && pos.y >= 0 && pos.y < int(H) && a > 0) {
                        const CRGB& c = rect(uint16_t(pos.x), uint16_t(pos.y));
                        r += uint16_t(c.r) * a;
                        g += uint16_t(c.g) * a;
                        b += uint16_t(c.b) * a;
                        a_sum += a;
                    }
                }
            }
            if (a_sum == 0) {
                leds[i] = CRGB::Black;
            } else {
                leds[i].r = uint8_t(r / a_sum);
                leds[i].g = uint8_t(g / a_sum);
                leds[i].b = uint8_t(b / a_sum);
            }
        }
    }

    void setup() {
        FastLED.addLeds<WS2812B, 5, GRB>(leds, NUM_LEDS);
    }

    void loop() {
        uint32_t t = fl::millis();
        draw_pattern(t);
        project_to_strip();
        FastLED.show();
    }
    ```

    2) Automate with task::before_frame (draw right before render)

    Use the per‑frame task API; no direct EngineEvents binding is needed. Per‑frame tasks are scheduled via `fl::task` and integrated with the frame lifecycle by the async system.

    ```cpp
    #include <FastLED.h>
    #include "fl/task.h"
    #include "fl/async.h"
    #include "fl/gfx/corkscrew.h"
    #include "fl/grid.h"

    constexpr uint16_t NUM_LEDS = 144;
    CRGB leds[NUM_LEDS];

    fl::Corkscrew cork(fl::Corkscrew::Input(19.0f, NUM_LEDS));
    const uint16_t W = cork.cylinder_width();
    const uint16_t H = cork.cylinder_height();
    fl::Grid<CRGB> rect(W, H);

    static void draw_pattern(uint32_t t_ms, fl::Grid<CRGB>& dst);
    static void project_to_strip(const fl::Corkscrew& c, const fl::Grid<CRGB>& src, CRGB* out, uint16_t n);

    void setup() {
        FastLED.addLeds<WS2812B, 5, GRB>(leds, NUM_LEDS);

        // Register a before_frame task that runs immediately before each render
        fl::task::before_frame().then([&](){
            uint32_t t = fl::millis();
            draw_pattern(t, rect);
            project_to_strip(cork, rect, leds, NUM_LEDS);
        });
    }

    void loop() {
        // The before_frame task is invoked automatically at the right time
        FastLED.show();
        // Optionally pump other async work
        fl::async_yield();
    }
    ```

    - The manual approach gives explicit control each frame.
    - The `task::before_frame()` approach schedules work just‑in‑time before rendering without manual event wiring. Use `task::after_frame()` for post‑render work.

- **High‑definition HSV16**
  - Headers: `hsv16.h`, implementation in `hsv16.cpp`
  - `fl::HSV16` stores 16‑bit H/S/V for high‑precision conversion to `CRGB` without banding; construct from `CRGB` or manually, and convert via `ToRGB()` or implicit cast.
  - Color boost:
    - `HSV16::colorBoost(EaseType saturation_function, EaseType luminance_function)` applies a saturation‑space boost similar to gamma, tuned separately for saturation and luminance to counter LED gamut/compression (e.g., WS2812).

- **Easing functions (accurate 8/16‑bit)**
  - Header: `ease.h`
  - Accurate ease‑in/out functions with 8‑ and 16‑bit variants for quad/cubic/sine families, plus dispatchers: `ease8(type, ...)`, `ease16(type, ...)`.
  - Use to shape animation curves, palette traversal, or time alpha outputs.

- **Time alpha**
  - Header: `time_alpha.h`
  - Helpers for time‑based interpolation: `time_alpha8/16/f` compute progress in a window `[start, end]`.
  - Stateful helpers:
    - `TimeRamp(rise, latch, fall)` for a full rise‑hold‑fall cycle.
    - `TimeClampedTransition(duration)` for a clamped one‑shot.
  - Use cases: envelope control for brightness, effect blending, or gating simulation energy over time.

### JSON Utilities

- Safe JSON access: `json.h`

Why: Ergonomic, crash‑resistant access with defaulting operator (`|`). Prefer the `fl::json` API for new code.

```cpp
fl::json cfg = fl::json::parse("{\"enabled\":true}");
bool enabled = cfg["enabled"] | false;
```

### I/O and Filesystem

- Basic I/O and streams: `io.h`, `ostream.h`, `istream.h`, `sstream.h`, `printf.h`
- Filesystem adapters: `file_system.h`

Why: Cross‑platform text formatting, buffered I/O, and optional file access on host builds.

### Algorithms and Utilities

- Algorithms and transforms: `algorithm.h`, `transform.h`, `range_access.h`
- Compile‑time utilities: `type_traits.h`, `tuple.h`, `variant.h`, `optional.h`, `utility.h`, `initializer_list.h`, `template_magic.h`, `types.h`, `stdint.h`
- Platform shims and control: `compiler_control.h`, `force_inline.h`, `virtual_if_not_avr.h`, `has_define.h`, `register.h`, `warn.h`, `trace.h`, `dbg.h`, `assert.h`, `unused.h`, `export.h`, `dll.h`

Why: Familiar patterns with embedded‑appropriate implementations and compiler‑portable controls.

### Audio and Reactive Systems

### UI System (JSON UI)

FastLED includes a JSON‑driven UI layer that can expose controls (sliders, buttons, checkboxes, number fields, dropdowns, titles, descriptions, help, audio visualizers) for interactive demos and remote control.

- **Availability by platform**
  - **AVR and other low‑memory chipsets**: disabled by default. The UI is not compiled in on constrained targets.
  - **WASM build**: enabled. The web UI is available and connects to the running sketch.
  - **Other platforms**: can be enabled if the platform supplies a bridge. See `platforms/ui_defs.h` for the compile‑time gate and platform includes.

- **Key headers and switches**
  - `platforms/ui_defs.h`: controls `FASTLED_USE_JSON_UI` (defaults to 1 on WASM, 0 elsewhere).
  - `fl/ui.h`: C++ UI element classes (UISlider, UIButton, UICheckbox, UINumberField, UIDropdown, UITitle, UIDescription, UIHelp, UIAudio, UIGroup).
  - `platforms/shared/ui/json/ui_manager.h`: platform‑agnostic JSON UI manager that integrates with `EngineEvents`.
  - `platforms/shared/ui/json/readme.md`: implementation guide and JSON protocol.

- **Lifecycle and data flow**
  - The UI system uses an update manager (`JsonUiManager`) and is integrated with `EngineEvents`:
    - On frame lifecycle events, new UI elements are exported as JSON; inbound updates are processed after frames.
    - This enables remote control: UI state can be driven locally (browser) or by remote senders issuing JSON updates.
  - Send (sketch → UI): when components are added or changed, `JsonUiManager` emits JSON to the platform bridge.
  - Receive (UI → sketch): the platform calls `JsonUiManager::updateUiComponents(const char*)` with a JSON object; changes are applied on `onEndFrame()`.

- **Registering handlers to send/receive JSON**
  - Platform bridge constructs the manager with a function that forwards JSON to the UI:
    - `JsonUiManager(Callback updateJs)` where `updateJs(const char*)` transports JSON to the front‑end (WASM example uses JS bindings in `src/platforms/wasm/ui.cpp`).
  - To receive UI updates from the front‑end, call:
    - `MyPlatformUiManager::instance().updateUiComponents(json_str)` to queue changes; `onEndFrame()` applies them.

- **Sketch‑side usage examples**

  Basic setup with controls and groups:

  ```cpp
  #include <FastLED.h>
  #include "fl/ui.h"

  UISlider brightness("Brightness", 128, 0, 255);
  UICheckbox enabled("Enabled", true);
  UIButton reset("Reset");
  UIDropdown mode("Mode", {fl::string("Rainbow"), fl::string("Waves"), fl::string("Solid")});
  UITitle title("Demo Controls");
  UIDescription desc("Adjust parameters in real time");
  UIHelp help("# Help\nUse the controls to tweak the effect.");

  void setup() {
      // Group controls visually in the UI
      UIGroup group("Main", title, desc, brightness, enabled, mode, reset, help);

      // Callbacks are pumped via EngineEvents; they trigger when values change
      brightness.onChanged([](UISlider& s){ FastLED.setBrightness((uint8_t)s); });
      enabled.onChanged([](UICheckbox& c){ /* toggle effect */ });
      mode.onChanged([](UIDropdown& d){ /* change program by d.as_int() */ });
      reset.onClicked([](){ /* reset animation state */ });
  }
  ```

  Help component (see `examples/UITest/UITest.ino`):
  ```cpp
  UIHelp helpMarkdown(
      R"(# FastLED UI\nThis area supports markdown, code blocks, and links.)");
  helpMarkdown.setGroup("Documentation");
  ```

  Audio input UI (WASM‑enabled platforms):
  ```cpp
  UIAudio audio("Mic");
  void loop() {
      while (audio.hasNext()) {
          auto sample = audio.next();
          // Use sample data for visualizations
      }
      FastLED.show();
  }
  ```

- **Platform bridge: sending and receiving JSON**
  - Sending from sketch to UI: `JsonUiManager` invokes the `updateJs(const char*)` callback with JSON representing either:
    - A JSON array of element definitions (on first export)
    - A JSON object of state updates (on changes)
  - Receiving from UI to sketch: the platform calls
    - `JsonUiManager::updateUiComponents(const char* jsonStr)` where `jsonStr` is a JSON object like:
      `{ "id_123": {"value": 200}, "id_456": {"pressed": true } }`
  - The manager defers application until the end of the current frame via `onEndFrame()` to ensure consistency.

- **WASM specifics**
  - WASM builds provide the web UI and JS glue (see `src/platforms/wasm/ui.cpp` and `src/platforms/wasm/compiler/modules/ui_manager.js`).
  - Element definitions may be routed directly to the browser UI manager to avoid feedback loops; updates are logged to an inspector, and inbound messages drive `updateUiComponents`.

- **Enabling on other platforms**
  - Implement a platform UI manager using `JsonUiManager` and provide the two weak functions the UI elements call:
    - `void addJsonUiComponent(fl::weak_ptr<JsonUiInternal>)`
    - `void removeJsonUiComponent(fl::weak_ptr<JsonUiInternal>)`
  - Forward platform UI events into `updateUiComponents(jsonStr)`.
  - Ensure `EngineEvents` are active so UI updates are exported and applied on frame boundaries.

- Audio adapters and analysis: `audio.h`, `audio_reactive.h`
- Engine hooks: `engine_events.h`

Why: Build effects that respond to input signals.

### Miscellaneous and Specialized

- Wave simulation: `wave_simulation.h`, `wave_simulation_real.h`
- Screen and LED glue: `leds.h`, `screenmap.h`
- Path/visual experimentation: `corkscrew.h`

---

## Guidance for New Users

- If you are writing sketches: include `FastLED.h` and follow examples in `examples/`. The `fl::` headers power those features under the hood.
- If you are extending FastLED internals or building advanced effects: prefer `fl::` containers and `fl::span` over STL equivalents to maintain portability.
- Favor smart pointers and moveable wrappers for resource management. Avoid raw pointers and manual `delete`.
- Use `fl::json` for robust JSON handling with safe defaults.

## Guidance for C++ Developers

- Treat `fl::` as an embedded‑friendly STL. Many concepts mirror the standard library but are tuned for constrained targets and consistent compiler support.
- When designing APIs, prefer non‑owning views (`fl::span<T>`) for input parameters and explicit ownership types for storage.
- Use `compiler_control.h` macros for warning control and portability instead of raw pragmas.

---

This README will evolve alongside the codebase. If you are exploring a specific subsystem, start from the relevant headers listed above and follow includes to supporting modules.
