Mathematical wave generator that produces per-LED colors from configurable waveform layers (sine, triangle, sawtooth, square) with superposition, mapped through a gradient palette. Supports sync clocks, bindable speed, and up to 8 wave layers. - Storage model with wave validation and apply_update - Numpy-vectorized stream with gradient LUT color mapping - API schemas (create/update/response) and route registration - Frontend editor with dynamic wave layer rows, gradient picker, speed widget, and IconSelect waveform selectors - i18n in en/ru/zh
5.1 KiB
math_wave Color Strip Source — Implementation Plan
Overview
A new CSS type that generates LED colors from configurable mathematical wave functions. Each LED position gets a wave value based on spatial position and time, mapped to a color via a gradient palette. Supports multiple superimposed wave layers, sync clocks, and bindable parameters.
Requirements
- Waveform types: sine, triangle, sawtooth, square
- Parameters per wave layer: waveform, frequency, amplitude, phase, offset
- Global parameters: speed (bindable), gradient_id (color mapping)
- Wave superposition: list of wave layers combined additively
- Spatial dimension: wave value depends on LED position (0.0-1.0) + time
- Sync clock integration for time parameter
- Color mapping: combined wave output (0.0-1.0) mapped through a gradient
Phase 1: Storage Model
File: server/src/wled_controller/storage/color_strip_source.py
- Add
MathWaveColorStripSourcedataclass afterGameEventColorStripSource - Fields:
waves: list— default[{"waveform": "sine", "frequency": 1.0, "amplitude": 1.0, "phase": 0.0, "offset": 0.0}]speed: BindableFloat— default 1.0gradient_id: Optional[str]— references Gradient entity
- Implement
to_dict,from_dict,create_from_kwargs,apply_update(followCandlelightColorStripSourcepattern) - Valid waveforms:
{"sine", "triangle", "sawtooth", "square"} - Add
"math_wave": MathWaveColorStripSourceto_SOURCE_TYPE_MAP
Phase 2: Stream Implementation
File: server/src/wled_controller/core/processing/math_wave_stream.py (new)
- Class
MathWaveColorStripStream(ColorStripStream)followingCandlelightColorStripStreampattern - Key methods:
__init__,_update_from_source,configure,start,stop,get_latest_colors,update_source,set_clock,set_gradient_store - Animation loop (
_animate_loop):- Get
tfrom clock (if set) or wall clock - For each LED at normalized position
p = i / (N-1):- Sum all wave layers:
sum += amplitude * waveform(2*pi*frequency*(p + speed*t) + phase) + offset - Clamp result to [0.0, 1.0]
- Sum all wave layers:
- Map per-LED values to RGB via gradient LUT
- Get
- Waveform functions (vectorized with numpy):
sine:0.5 + 0.5 * np.sin(x)triangle:2.0 * np.abs(np.mod(x / (2*pi), 1.0) - 0.5)sawtooth:np.mod(x / (2*pi), 1.0)square:(np.sin(x) >= 0).astype(float)
- Double-buffering pattern (same as candlelight)
- Use
self.resolve("speed", self._speed)for bindable speed - Gradient resolution via
set_gradient_storepattern
Phase 3: API Integration
File: server/src/wled_controller/api/schemas/color_strip_sources.py
- Add
MathWaveCSSResponse,MathWaveCSSCreate,MathWaveCSSUpdate - Add to
ColorStripSourceResponse,ColorStripSourceCreate,ColorStripSourceUpdateunions
File: server/src/wled_controller/core/processing/color_strip_stream_manager.py
- Import
MathWaveColorStripStream - Add
"math_wave": MathWaveColorStripStreamto_SIMPLE_STREAM_MAP - Existing
set_gradient_storeand_inject_clockinjection handles it automatically
Phase 4: Frontend
File: server/src/wled_controller/static/js/types.ts
- Add
'math_wave'toCSSSourceTypeunion
File: server/src/wled_controller/static/js/core/icons.ts
- Add
math_wave: _svg(P.activity)to_colorStripTypeIcons
File: server/src/wled_controller/templates/modals/css-editor.html
- Add
<option value="math_wave">to type select - Add
<div id="css-editor-math-wave-section">with:- Gradient picker (EntitySelect)
- Speed (BindableScalarWidget)
- Wave layers list (dynamic rows: waveform IconSelect, frequency/amplitude/phase/offset inputs, add/remove buttons)
File: server/src/wled_controller/static/js/features/color-strips.ts
- Add to
CSS_TYPE_KEYS,CSS_SECTION_MAP,CSS_TYPE_SETUP,NON_PICTURE_TYPES - Add to
clockTypesarray insaveCSSEditor - Add type handler:
load(css),reset(),getPayload(name) - Add card renderer showing wave count, gradient swatch, speed, clock badge
- Add wave layer management:
_renderMathWaveRow,addMathWaveLayer,removeMathWaveLayer
Files: en.json, ru.json, zh.json
- Add i18n keys for type name, description, all field labels, waveform names
Phase 5: Testing
File: server/tests/core/test_math_wave_stream.py (new)
- Test wave functions produce expected values at known inputs
- Test single wave spatial pattern
- Test wave superposition
- Test gradient color mapping
- Test clock integration
- Test
update_sourcehot-update - Test
configureauto-sizing
File: server/tests/e2e/test_color_strip_flow.py
- Add
test_math_wave_crudto lifecycle tests
Storage model tests:
- Test
from_dictroundtrip - Test
create_from_kwargswith valid/invalid waveforms - Test
apply_update - Test
_SOURCE_TYPE_MAPdispatch
Risks & Mitigations
- Wave layer UI complexity — Follow existing composite layers / game event mappings patterns
- Performance with many layers — Vectorize with numpy; cap max wave layers to 8
- Gradient resolution — Stream manager already injects
set_gradient_storeautomatically