From f96c2087d9f12f2fc87168b1572408a396902b85 Mon Sep 17 00:00:00 2001 From: Joachim Dick <62520542+JoaDick@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:19:42 +0100 Subject: [PATCH] New smooth effect: Color Clouds (#5268) * New Effect: Color Clouds ColorClouds: Random start points for clouds and color ColorClouds: new config option 'More red' * ColorClouds: Incorporated review comments - Support for color palettes - Use perlin16() instead of inoise16() - Use cos8_t() instead of cos8() * ColorClouds: incorporated more review comments * ColorClouds: incorporated final review comment --- wled00/FX.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ wled00/FX.h | 3 +- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index f2ee0d4a..4399dbc1 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5036,6 +5036,85 @@ uint16_t mode_aurora(void) { } static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pal=50"; + +/** Softly floating colorful clouds. + * This is a very smooth effect that moves colorful clouds randomly around the LED strip. + * It was initially intended for rather unobtrusive ambient lights (with very slow speed settings). + * Nevertheless, it appears completely different and quite vibrant when the sliders are moved near + * to their limits. No matter in which direction or in which combination... + * Ported to WLED from https://github.com/JoaDick/EyeCandy/blob/master/ColorClouds.h + */ +uint16_t mode_ColorClouds() +{ + // Set random start points for clouds and color. + if (SEGENV.call == 0) { + SEGENV.aux0 = hw_random16(); + SEGENV.aux1 = hw_random16(); + } + const uint32_t volX0 = SEGENV.aux0; + const uint32_t hueX0 = SEGENV.aux1; + const uint8_t hueOffset0 = volX0 + hueX0; // derive a 3rd random number + + // Makes a very soft wraparound of the color palette by putting more emphasis on the begin & end + // of the palette (or on the red'ish colors in case of a rainbow spectrum). + // This gives the effect oftentimes an even more calm perception. + const bool cozy = SEGMENT.check3; + + // Higher values make the clouds move faster. + const uint32_t volSpeed = 1 + SEGMENT.speed; + + // Higher values make the color change faster. + const uint32_t hueSpeed = 1 + SEGMENT.intensity; + + // Higher values make more clouds (but smaller ones). + const uint32_t volSqueeze = 8 + SEGMENT.custom1; + + // Higher values make the clouds more colorful. + const uint32_t hueSqueeze = SEGMENT.custom2; + + // Higher values make larger gaps between the clouds. + const int32_t volCutoff = 12500 + SEGMENT.custom3 * 900; + const int32_t volSaturate = 52000; + // Note: When adjusting these calculations, ensure that volCutoff is always smaller than volSaturate. + + const uint32_t now = strip.now; + const uint32_t volT = now * volSpeed / 8; + const uint32_t hueT = now * hueSpeed / 8; + const uint8_t hueOffset = beat88(64) >> 8; + + bool doGammaCorrection = false; // WLEDMM gamma correction only needed when color is _not_ from a palette + if (SEGMENT.palette == 0) doGammaCorrection = true; + for (int i = 0; i < SEGLEN; i++) { + const uint32_t volX = i * volSqueeze * 64; + int32_t vol = perlin16(volX0 + volX, volT); + vol = map(vol, volCutoff, volSaturate, 0, 255); + vol = constrain(vol, 0, 255); + + const uint32_t hueX = i * hueSqueeze * 8; + uint8_t hue = perlin16(hueX0 + hueX, hueT) >> 7; + hue += hueOffset0; + hue += hueOffset; + if (cozy) { + hue = cos8_t(128 + hue / 2); + } + + uint32_t pixel; + if (SEGMENT.palette) { pixel = SEGMENT.color_from_palette(hue, false, true, 0, vol); } + else { hsv2rgb(CHSV32(hue, 255, vol), pixel); } + + // Suppress extremely dark pixels to avoid flickering of plain r/g/b. + if (int(R(pixel)) + G(pixel) + B(pixel) <= 2) { + pixel = 0; + } + + if (doGammaCorrection) pixel = gamma32(pixel); + SEGMENT.setPixelColor(i, pixel); + } + return FRAMETIME; +} +static const char _data_FX_MODE_COLORCLOUDS[] PROGMEM = "Color Clouds@!,!,Clouds,Colors,Distance,,,Cozy;;!;;sx=24,ix=32,c1=48,c2=64,c3=12,pal=0"; + + // WLED-SR effects ///////////////////////// @@ -12155,6 +12234,7 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_COLOR_SWEEP_RANDOM, &mode_color_sweep_random, _data_FX_MODE_COLOR_SWEEP_RANDOM); addEffect(FX_MODE_RUNNING_COLOR, &mode_running_color, _data_FX_MODE_RUNNING_COLOR); addEffect(FX_MODE_AURORA, &mode_aurora, _data_FX_MODE_AURORA); + addEffect(FX_MODE_COLORCLOUDS, &mode_ColorClouds, _data_FX_MODE_COLORCLOUDS); addEffect(FX_MODE_RUNNING_RANDOM, &mode_running_random, _data_FX_MODE_RUNNING_RANDOM); addEffect(FX_MODE_LARSON_SCANNER, &mode_larson_scanner, _data_FX_MODE_LARSON_SCANNER); addEffect(FX_MODE_COMET, &mode_comet, _data_FX_MODE_COMET); diff --git a/wled00/FX.h b/wled00/FX.h index c718c092..d43c47a8 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -399,7 +399,8 @@ static uint8_t strip_getPaletteBlend(); // forward declaration: little helper t #define FX_MODE_PS1DSPRINGY 227 #define FX_MODE_PARTICLEGALAXY 228 -#define MODE_COUNT 229 +#define FX_MODE_COLORCLOUDS 229 +#define MODE_COUNT 230 typedef enum mapping1D2D { M12_Pixels = 0,