Skip to content

Animated Staircase: segments not fading out but switched on/off instantly in version 16.0.0 and 16.0.1  #5731

Open

Description

@Alfagek

What happened?

From version 16 i have a strange behaivor with the staircase mod on my esp32.
When the sensor is getting triggert, it's putting all the led segments on, and you see on background off the leds it's "fading" each segment. And that's also with going out, you see a little fade en then all the segements going out.

When i switch back to a version below 16 its working all fine.

To Reproduce Bug

Working all fine with versions below 16. From version 16 it's starting. Also not fixt in 16.0.1.

Expected Behavior

fading on/of each segment. instead of full on and a little "fade" in de background

Install Method

Self-Compiled

What version of WLED?

WLED 16.0.1 2605010

Which microcontroller/board are you seeing the problem on?

ESP32

Relevant log/trace output

Anything else?

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. changed the title [-]version 16,0.0 and 16.0.1 Animated Staircase bug[/-] [+]version 16.0.0 and 16.0.1 Animated Staircase bug[/+] on Jul 10, 2026
  2. DedeHai commented on Jul 10, 2026

    @DedeHai
    Collaborator

    not really clear what you mean, a video comparison would help

  3. Alfagek commented on Jul 10, 2026

    @Alfagek
    Author

    not really clear what you mean, a video comparison would help

    Version below 16 (like it should work, this is version 15.4): https://share.icloud.com/photos/064XZHw-sHwW6zwc21e8DooTw

    Version 16.0.0 and also 16.0.1: https://share.icloud.com/photos/0c1obqgxZSY6P82gULVyMyfhQ

    All the segments are going on, and then you see a little bit brightness differance in the segments going up. And if i upload version 15.4 back it's working like it should be.

    Hopely it's a bit more clear now ;-)

  4. softhack007 commented on Jul 21, 2026

    @softhack007
    Member

    @coderabbitai please compare the /usermods/Animated_Staircase between the 16_x and 0_15_x branch, and look for differences that can explain the behaviour observed by the user. Also take difference in the wled core into consideration, especially the new segment blending and effect transitions code, and different handling of gamma correction. Assume a nominal configuration witch several segments running the "solid" effect at high brightness.

  5. coderabbitai commented on Jul 21, 2026

    @coderabbitai
    Contributor
    🧩 Analysis chain

    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    git branch -a | sed -n '1,160p'
    
    echo '--- Animated_Staircase locations ---'
    fd -HI -t f 'Animated_Staircase|animated_staircase' . | sort
    
    echo '--- branch file inventories ---'
    for branch in 0_15_x 16_x origin/0_15_x origin/16_x; do
      if git rev-parse --verify --quiet "${branch}^{commit}" >/dev/null; then
        echo "[$branch]"
        git ls-tree -r --name-only "$branch" | grep -i 'Animated_Staircase\|FX\|colors\|segment' | head -160
      fi
    done
    
    echo '--- changed files between branches ---'
    left="$(git rev-parse --verify --quiet 0_15_x^{commit} || git rev-parse --verify origin/0_15_x^{commit})"
    right="$(git rev-parse --verify --quiet 16_x^{commit} || git rev-parse --verify origin/16_x^{commit})"
    git diff --name-status "$left" "$right" | grep -Ei 'Animated_Staircase|wled00/.*(FX|color|segment|led|json)|usermod' || true

    Length of output: 1454


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    echo '--- matching upstream refs ---'
    git ls-remote --heads https://github.com/wled/WLED.git \
      | grep -E 'refs/heads/(0_15_x|16_x|v0\.15|v0\.16)' || true
    
    echo '--- GitHub compare metadata and focused changed paths ---'
    gh api 'repos/wled/WLED/compare/0_15_x...16_x' \
      --jq '{status, ahead_by, behind_by, total_commits, files: [.files[]? | {filename,status,previous_filename} | select(.filename | test("(^usermods/Animated_Staircase/|^wled00/.*(FX|color|segment|led|json)|gamma|blend|transition)"; "i"))]}' \
      || true
    
    echo '--- current usermod structure (symbol map only) ---'
    ast-grep outline usermods/Animated_Staircase/Animated_Staircase.cpp --items all --view expanded

    Length of output: 4157


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    old_ref='53a4510d310b5c175bca98098a562349003e0c64'
    new_ref='f05febc3400d6f41317152c523e858d6f1583787'
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    get_file() {
      local ref="$1" path="$2" out="$3"
      gh api "repos/wled/WLED/contents/${path}?ref=${ref}" --jq '.content' | tr -d '\n' | base64 -d > "$out"
    }
    
    get_file "$old_ref" 'usermods/Animated_Staircase/Animated_Staircase.h' "$workdir/stair-15.h"
    get_file "$new_ref" 'usermods/Animated_Staircase/Animated_Staircase.cpp' "$workdir/stair-16.cpp"
    for file in 'wled00/FX.cpp' 'wled00/FX.h' 'wled00/FX_fcn.cpp' 'wled00/colors.cpp' 'wled00/colors.h'; do
      name="${file//\//_}"
      get_file "$old_ref" "$file" "$workdir/15-$name"
      get_file "$new_ref" "$file" "$workdir/16-$name"
    done
    
    echo '--- Animated Staircase diff ---'
    git diff --no-index -- "$workdir/stair-15.h" "$workdir/stair-16.cpp" || true
    
    echo '--- core symbol maps ---'
    for f in "$workdir"/{15,16}-wled00_{FX.cpp,FX_fcn.cpp,colors.cpp}; do
      echo "[$(basename "$f")]"
      ast-grep outline "$f" --lang cpp --items all --view compact | grep -Ei 'Segment|solid|blend|transition|gamma|setPixel|show|service|render' || true
    done
    
    echo '--- focused core line references ---'
    for f in "$workdir"/{15,16}-wled00_{FX.cpp,FX.h,FX_fcn.cpp,colors.cpp,colors.h}; do
      echo "[$(basename "$f")]"
      rg -n -i -C 2 'gamma|blend|transition|\bsolid\b|setPixelColor|setPixelColorXY|show\(' "$f" | head -260 || true
    done

    Length of output: 1797


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    old_ref='53a4510d310b5c175bca98098a562349003e0c64'
    new_ref='f05febc3400d6f41317152c523e858d6f1583787'
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    for label_ref in "15:$old_ref" "16:$new_ref"; do
      label="${label_ref%%:*}"
      ref="${label_ref#*:}"
      echo "--- ${label} branch: matching tree paths ---"
      gh api "repos/wled/WLED/git/trees/${ref}?recursive=1" \
        --jq '.tree[] | select(.type == "blob") | .path | select(test("Animated_Staircase|^wled00/(FX(\\.cpp|\\.h|_fcn\\.cpp)|colors(\\.cpp|\\.h))$"; "i"))' \
        | sort
    done
    
    echo '--- candidate source availability ---'
    for ref in "$old_ref" "$new_ref"; do
      echo "[$ref]"
      gh api "repos/wled/WLED/git/trees/${ref}?recursive=1" \
        --jq '.tree[] | select(.type == "blob") | [.path,.sha] | `@tsv`' \
        | grep -E 'Animated_Staircase|^wled00/(FX(\.cpp|\.h|_fcn\.cpp)|colors(\.cpp|\.h))$' \
        || true
    done

    Length of output: 2022


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    get_blob() {
      local sha="$1" out="$2"
      gh api "repos/wled/WLED/git/blobs/${sha}" --jq '.content' | tr -d '\n' | base64 -d > "$out"
    }
    
    get_blob '54a9b3331eb5534c62d65a5c95728f7aac7abeab' "$workdir/stair-15.h"
    get_blob '2d2d27cf43fb64940fb5489915ddb25087d59ae5' "$workdir/stair-16.cpp"
    
    for ref_label in \
      '53a4510d310b5c175bca98098a562349003e0c64:15' \
      'f05febc3400d6f41317152c523e858d6f1583787:16'; do
      ref="${ref_label%%:*}"
      label="${ref_label#*:}"
      tree="$workdir/tree-$label.tsv"
      gh api "repos/wled/WLED/git/trees/${ref}?recursive=1" \
        --jq '.tree[] | select(.type == "blob") | [.path,.sha] | `@tsv`' > "$tree"
      for path in 'wled00/FX.cpp' 'wled00/FX.h' 'wled00/FX_fcn.cpp' 'wled00/colors.cpp'; do
        sha="$(awk -F '\t' -v p="$path" '$1 == p {print $2}' "$tree")"
        get_blob "$sha" "$workdir/$label-${path//\//_}"
      done
    done
    
    echo '--- complete Animated Staircase difference ---'
    git diff --no-index --label '0_15_x/Animated_Staircase.h' --label '16_x/Animated_Staircase.cpp' \
      "$workdir/stair-15.h" "$workdir/stair-16.cpp" || true
    
    echo '--- core function maps relevant to frame rendering ---'
    for f in "$workdir"/{15,16}-wled00_{FX.cpp,FX_fcn.cpp,colors.cpp}; do
      echo "[$(basename "$f")]"
      ast-grep outline "$f" --lang cpp --items function --view compact \
        | grep -Ei 'Segment|solid|blend|transition|gamma|setPixel|show|service|render|color' || true
    done
    
    echo '--- locations of relevant core operations ---'
    for f in "$workdir"/{15,16}-wled00_{FX.cpp,FX_fcn.cpp,colors.cpp}; do
      echo "[$(basename "$f")]"
      rg -n -i 'gamma|blend|transition|\bsolid\b|setPixelColor|setPixelColorXY|show\(' "$f" || true
    done

    Length of output: 117118


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    get_blob() {
      gh api "repos/wled/WLED/git/blobs/$1" --jq '.content' | tr -d '\n' | base64 -d > "$2"
    }
    tree_to_sources() {
      local ref="$1" label="$2"
      local tree="$workdir/tree-$label.tsv"
      gh api "repos/wled/WLED/git/trees/${ref}?recursive=1" \
        --jq '.tree[] | select(.type == "blob") | [.path,.sha] | `@tsv`' > "$tree"
      for path in 'wled00/FX.cpp' 'wled00/FX_fcn.cpp' 'wled00/colors.cpp'; do
        sha="$(awk -F '\t' -v p="$path" '$1 == p {print $2}' "$tree")"
        get_blob "$sha" "$workdir/$label-${path//\//_}"
      done
    }
    
    get_blob '54a9b3331eb5534c62d65a5c95728f7aac7abeab' "$workdir/stair-15.h"
    get_blob '2d2d27cf43fb64940fb5489915ddb25087d59ae5' "$workdir/stair-16.cpp"
    tree_to_sources '53a4510d310b5c175bca98098a562349003e0c64' '15'
    tree_to_sources 'f05febc3400d6f41317152c523e858d6f1583787' '16'
    
    echo '--- Animated Staircase unified diff ---'
    diff -u "$workdir/stair-15.h" "$workdir/stair-16.cpp" || true
    
    echo '--- static (Solid) effect implementation ---'
    for f in "$workdir"/{15,16}-wled00_FX.cpp; do
      echo "[$(basename "$f")]"
      static_line="$(rg -n 'mode_static\s*\(' "$f" | head -1 | cut -d: -f1)"
      if [ -n "$static_line" ]; then
        sed -n "$((static_line-3)),$((static_line+70))p" "$f"
      fi
    done
    
    echo '--- render/composite pipeline ---'
    for spec in \
      "15:$workdir/15-wled00_FX_fcn.cpp:1375:1490" \
      "16:$workdir/16-wled00_FX_fcn.cpp:1318:1745"; do
      IFS=: read -r label file start end <<< "$spec"
      echo "[$label $(basename "$file") lines $start-$end]"
      sed -n "${start},${end}p" "$file"
    done
    
    echo '--- gamma and blend primitive differences ---'
    diff -u "$workdir/15-wled00_colors.cpp" "$workdir/16-wled00_colors.cpp" \
      | grep -E -C 5 'gammaT|gammaT_inv|Correct32|inverseGamma32|color_blend' \
      | head -280 || true

    Length of output: 42437


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    get_blob() {
      gh api "repos/wled/WLED/git/blobs/$1" --jq '.content' | tr -d '\n' | base64 -d > "$2"
    }
    get_source() {
      local ref="$1" label="$2" path="$3"
      local sha
      sha="$(gh api "repos/wled/WLED/git/trees/${ref}?recursive=1" \
        --jq ".tree[] | select(.path == \"${path}\" and .type == \"blob\") | .sha")"
      get_blob "$sha" "$workdir/$label-${path//\//_}"
    }
    
    get_blob '54a9b3331eb5534c62d65a5c95728f7aac7abeab' "$workdir/stair-15.h"
    get_blob '2d2d27cf43fb64940fb5489915ddb25087d59ae5' "$workdir/stair-16.cpp"
    get_source '53a4510d310b5c175bca98098a562349003e0c64' '15' 'wled00/FX_fcn.cpp'
    get_source 'f05febc3400d6f41317152c523e858d6f1583787' '16' 'wled00/FX_fcn.cpp'
    
    echo '--- staircase segment-control calls (both versions) ---'
    for f in "$workdir"/stair-*; do
      echo "[$(basename "$f")]"
      rg -n -C 8 'onIndex|offIndex|setOption|setMode|setColor|SEG_OPTION_ON|strip\.getSegment' "$f"
    done
    
    echo '--- on/off transition and brightness methods ---'
    for f in "$workdir"/{15,16}-wled00_FX_fcn.cpp; do
      echo "[$(basename "$f")]"
      for symbol in 'currentBri' 'currentColor' 'setOption'; do
        line="$(rg -n "Segment::${symbol}\s*\(" "$f" | head -1 | cut -d: -f1)"
        if [ -n "$line" ]; then
          echo "### ${symbol} (line ${line})"
          sed -n "$((line-4)),$((line+65))p" "$f"
        fi
      done
    done
    
    echo '--- transition defaults/configuration references ---'
    for f in "$workdir"/{15,16}-wled00_FX_fcn.cpp; do
      echo "[$(basename "$f")]"
      rg -n -C 3 'blendingStyle|TRANSITION_FADE|fadeTransition|SEG_OPTION_ON' "$f" | head -260
    done

    Length of output: 27255


    @softhack007, comparison complete. The likely explanation is the WLED 16 core transition/compositing change, not a functional change in Animated_Staircase.

    Animated Staircase: no behavioral regression found

    Between 0_15_x and 16_x, the usermod logic is unchanged:

    • updateSegments() still enables/disables each active segment through seg.setOption(SEG_OPTION_ON, ...).
    • updateSwipe() still advances the staircase one segment per segment_delay_ms.
    • Enabling the usermod still sets the global transition duration to segment_delay_ms (default: 150 ms):
    transitionDelay = segment_delay_ms;
    strip.setTransition(segment_delay_ms);

    The only source differences are removing #pragma once after changing the file from .h to .cpp, and adding static usermod registration.

    Why WLED 16 can show the reported brightness staircase

    Solid itself remains unchanged in both branches:

    uint16_t mode_static(void) {
      SEGMENT.fill(SEGCOLOR(0));
      return strip.isOffRefreshRequired() ? FRAMETIME : 350;
    }

    However, the rendering route changed substantially.

    WLED 15 WLED 16
    Effects write directly to the bus output. Effects write into an individual segment buffer.
    Segment colors are gamma-corrected before the effect runs. Segment buffers are composited, then final output gamma correction is applied.
    currentBri() exists, but Solid filling SEGCOLOR(0) does not itself scale its pixels by that transitional brightness. blendSegment() uses topSegment.currentBri() as the segment opacity during composition.
    On/off transitions therefore do not produce the same physically interpolated per-segment fade path. Each newly enabled segment is visibly faded in, including during Solid.

    In 16, WS2812FX::blendSegment() does this:

    uint8_t opacity = topSegment.currentBri();
    if (gammaCorrectCol) opacity = gamma8inv(opacity);

    It blends the segment into the frame buffer with that opacity, and WS2812FX::show() then applies gamma32() to the final pixel output.

    So the unchanged usermod sequentially starts an on-transition for every stair segment, while 16 now actually composites that transition as segment opacity. With several high-brightness Solid segments, the latest segment(s) remain visibly dimmer until their 150 ms fade completes. Gamma-correct compositing makes that partial-ramp difference especially obvious.

    Conclusion

    This explains a temporary brightness gradient during the upward/downward sweep:

    1. Staircase enables a segment.
    2. WLED 16 fades that segment from black to its Solid color.
    3. The next segment starts shortly after.
    4. Previously activated segments are closer to full opacity than newly activated ones.

    That behavior is consistent with the supplied 16.0.x video and would not appear equivalently on 15.4 because the old direct-rendering pipeline did not apply the same per-segment opacity composition to Solid.

    Recommended verification / mitigation

    • Set the transition duration to 0 (or otherwise bypass transitions) as a diagnostic. If the brightness differences disappear, this confirms the transition-compositing path.
    • Alternatively, make the staircase usermod turn segments on immediately while retaining its own segment_delay_ms as only the step timing.
    • If brightness remains unequal after the whole swipe plus transition time has elapsed, this analysis does not explain it. Then inspect:
      • automatic brightness limiting / power supply limiting,
      • segment opacity and blend mode,
      • segment overlap, grouping, or spacing,
      • LED power injection and wiring.

    The gamma redesign is relevant primarily because it changes the visual shape of partial fades; it should not leave otherwise identical, fully settled, non-overlapping Solid segments at different brightness.

  6. softhack007 commented on Jul 21, 2026

    @softhack007
    Member

    @DedeHai It looks like the usermod changes the segment "on" state directly. Could the problem be related to the known limitation that segment on/off does not use transitions/fading in 16.0?

    // shorten the strip transition time to be equal or shorter than segment delay
    transitionDelay = segment_delay_ms;
    strip.setTransition(segment_delay_ms);
    strip.trigger();

    if (i >= onIndex && i < offIndex) {
    seg.setOption(SEG_OPTION_ON, true);
    // We may need to copy mode and colors from segment 0 to make sure
    // changes are propagated even when the config is changed during a wipe
    // seg.setMode(mainsegment.mode);
    // seg.setColor(0, mainsegment.colors[0]);
    } else {
    seg.setOption(SEG_OPTION_ON, false);
    }

    if (oldOn != onIndex || oldOff != offIndex) {
    updateSegments(); // reduce the number of updates to necessary ones
    if (togglePower && onIndex == offIndex && !offMode && !on) toggleOnOff(); // toggle power off for all segments off
    }

    @coderabbitai did 0.15.x use cross-fading when segments were toggled on/off ?

  7. coderabbitai commented on Jul 21, 2026

    @coderabbitai
  8. coderabbitai commented on Jul 21, 2026

    @coderabbitai
    Contributor
    🧩 Analysis chain

    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    repo='wled/WLED'
    old_ref='0_15_x'
    new_ref='16_x'
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    echo '--- issue `#5520` ---'
    gh issue view 5520 --repo "$repo" --comments
    echo '--- issue `#5524` ---'
    gh issue view 5524 --repo "$repo" --comments
    
    fetch_file() {
      local ref="$1" path="$2" label="$3"
      gh api "repos/${repo}/contents/${path}?ref=${ref}" --jq '.content' \
        | tr -d '\n' | base64 -d > "${workdir}/${label}-$(basename "$path")"
    }
    
    for ref_label in "${old_ref}:15" "${new_ref}:16"; do
      ref="${ref_label%%:*}"
      label="${ref_label#*:}"
      fetch_file "$ref" 'wled00/FX_fcn.cpp' "$label"
      fetch_file "$ref" 'wled00/FX.cpp' "$label"
      fetch_file "$ref" 'wled00/FX.h' "$label"
    done
    
    echo '--- relevant definitions and callers: 0_15_x / 16_x ---'
    for f in "$workdir"/*; do
      echo "[$(basename "$f")]"
      rg -n -C 8 \
        'Segment::setOption|setOption\s*\(|SEG_OPTION_ON|currentBri\s*\(|transition|blendSegment|isActive' \
        "$f" | head -400 || true
    done
    
    echo '--- focused diffs for rendering and segment state ---'
    diff -u "$workdir/15-FX_fcn.cpp" "$workdir/16-FX_fcn.cpp" \
      | grep -E -C 10 'setOption|SEG_OPTION_ON|currentBri|blendSegment|transition|isActive' \
      | head -700 || true

    Length of output: 20138


    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    repo='wled/WLED'
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    echo '--- pull request `#5524` ---'
    gh pr view 5524 --repo "$repo" --json number,state,mergedAt,baseRefName,headRefName,title,url,body,commits,files \
      --jq '{number,state,mergedAt,baseRefName,headRefName,title,url,files:[.files[].path]}'
    
    echo '--- refs ---'
    git ls-remote --heads "https://github.com/${repo}.git" \
      | grep -E 'refs/heads/(0_15_x|16_x)$' || true
    
    fetch_file() {
      local ref="$1" path="$2" destination="$3"
      gh api "repos/${repo}/contents/${path}?ref=${ref}" --jq '.content' \
        | tr -d '\n' | base64 -d > "$destination"
    }
    
    for ref_label in '0_15_x:15' '16_x:16'; do
      ref="${ref_label%%:*}"
      label="${ref_label#*:}"
      for path in wled00/FX_fcn.cpp wled00/FX.cpp wled00/FX.h wled00/led.cpp; do
        if fetch_file "$ref" "$path" "$workdir/${label}-$(basename "$path")"; then
          echo "Fetched ${ref}:${path}"
        else
          echo "Could not fetch ${ref}:${path}" >&2
        fi
      done
    done
    
    echo '--- 0_15_x segment state / transition implementation ---'
    for f in "$workdir"/15-*; do
      [ -f "$f" ] || continue
      echo "[$(basename "$f")]"
      rg -n -C 12 'Segment::setOption|Segment::startTransition|Segment::currentBri|SEG_OPTION_ON|setTransitionMode|handleTransitions' "$f" || true
    done
    
    echo '--- corresponding WLED 16_x implementation ---'
    for f in "$workdir"/16-*; do
      [ -f "$f" ] || continue
      echo "[$(basename "$f")]"
      rg -n -C 12 'Segment::setOption|Segment::startTransition|Segment::currentBri|SEG_OPTION_ON|setTransitionMode|handleTransitions|blendSegment' "$f" || true
    done
    
    echo '--- PR `#5524` patch locations ---'
    gh pr diff 5524 --repo "$repo" -- wled00/FX_fcn.cpp wled00/led.cpp wled00/FX.cpp 2>/dev/null \
      | sed -n '1,700p' || true

    Length of output: 26955


    @softhack007, yes — in 0_15_x, toggling a segment’s on option was designed to cross-fade, provided that fade transitions were enabled and the configured transition duration was non-zero.

    The relevant 0.15.x path is:

    Segment &Segment::setOption(uint8_t n, bool val) {
      bool prevOn = on;
      if (fadeTransition && n == SEG_OPTION_ON && val != prevOn)
        startTransition(strip.getTransition()); // before changing `on`
    
      if (val) options |=   0x01 << n;
      else     options &= ~(0x01 << n);
      ...
    }

    During that transition, Segment::currentBri() interpolates from the saved old brightness to the new effective brightness:

    unsigned curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
    curBri += (useCct ? _t->_cctT : _t->_briT) * (0xFFFFU - prog);

    So an on-toggle fades from 0 → opacity, and an off-toggle from opacity → 0. In other words, the Animated Staircase usermod’s direct SEG_OPTION_ON updates could produce the expected per-segment fade on 0.15.x.

    This makes #5520 / #5524 highly relevant to the reported regression. WLED 16 retains calls to startTransition() in Segment::setOption(), but the 16.0.x transition/compositing lifecycle has a known defect: its global transition handling can stop per-segment transitions prematurely or otherwise fail to render non-Fade transition styles correctly. #5520’s reports match that: segment toggles snap immediately in 16.0.x, while Fade may be the exception. PR #5524 is explicitly titled “make per-segment on/off and brightness behave the same as global.”

    So I would revise the earlier conclusion:

    • 0.15.x: direct segment toggles cross-faded under the Fade transition path.
    • 16.0.x: the intended support exists, but the known segment-transition issue can prevent it from behaving equivalently.
    • Therefore, this is a plausible explanation for the Animated Staircase behavioral difference, and testing the usermod with the #5524 patch is the most targeted verification.
  9. coderabbitai commented on Jul 21, 2026

    @coderabbitai
  10. coderabbitai commented on Jul 21, 2026

    @coderabbitai
  11. softhack007 commented on Jul 21, 2026

    @softhack007
    Member

    @coderabbitai is transitionDelay the correct global, or should it be transitionDelayDefault ?

    // shorten the strip transition time to be equal or shorter than segment delay
    transitionDelay = segment_delay_ms;
    strip.setTransition(segment_delay_ms);
    strip.trigger();

  12. coderabbitai commented on Jul 21, 2026

    @coderabbitai
    Contributor
    🧩 Analysis chain

    🏁 Script executed:

    #!/bin/bash
    set -euo pipefail
    
    repo='wled/WLED'
    ref='05890314eb792368424fb4a01a7fe74f8395e085'
    workdir="$(mktemp -d)"
    trap 'rm -rf "$workdir"' EXIT
    
    fetch() {
      local path="$1"
      local destination="$2"
      gh api "repos/${repo}/contents/${path}?ref=${ref}" --jq '.content' |
        tr -d '\n' | base64 -d > "$destination"
    }
    
    fetch 'usermods/Animated_Staircase/Animated_Staircase.cpp' "$workdir/Animated_Staircase.cpp"
    
    echo '--- Cited usermod code (Lines 250–325) ---'
    sed -n '250,325p' "$workdir/Animated_Staircase.cpp"
    
    echo '--- Tree paths likely to declare/use transition globals ---'
    gh api "repos/${repo}/git/trees/${ref}?recursive=1" --jq '
      .tree[] | select(.type == "blob") | .path
      | select(test("^(wled00/|usermods/Animated_Staircase/)"))
    ' | sort > "$workdir/paths"
    
    while IFS= read -r path; do
      base="${path//\//_}"
      if gh api "repos/${repo}/contents/${path}?ref=${ref}" --jq '.content' 2>/dev/null |
           tr -d '\n' | base64 -d > "$workdir/$base"; then
        if rg -q 'transitionDelay(Default)?|setTransition\s*\(' "$workdir/$base"; then
          echo "--- ${path} ---"
          rg -n -C 5 'transitionDelay(Default)?|setTransition\s*\(' "$workdir/$base"
        fi
      fi
    done < "$workdir/paths"

    Length of output: 18048


    @softhack007, transitionDelayDefault is not the right one here. It is the persisted configuration default (cfg.json) and is restored during startup:

    WLED_GLOBAL uint16_t transitionDelayDefault _INIT(750); // default transition time (stored in cfg.json)

    For the currently active global transition setting, transitionDelay is the corresponding global:

    WLED_GLOBAL uint16_t transitionDelay _INIT(750); // global transition duration

    However, for Animated_Staircase, neither global alone controls the immediate segment-toggle transition: Segment::setOption() ultimately uses the duration held by strip, so this is the operative call:

    strip.setTransition(segment_delay_ms);

    Thus, the present transitionDelay assignment is semantically preferable to transitionDelayDefault, but it is redundant for the immediate effect because strip.setTransition() follows it.

    The more important issue is that the usermod currently does not restore the previous active transition duration when disabled. It therefore permanently changes the running transition duration to segment_delay_ms for the rest of the session (and transitionDelay may subsequently be serialized as current state).

    A safer approach is to save strip.getTransition() when enabling, set only the temporary strip transition for the staircase, and restore the saved value when disabling. transitionDelayDefault should remain untouched.

  13. changed the title [-]version 16.0.0 and 16.0.1 Animated Staircase bug[/-] [+]Animated Staircase: segments not fading out but switched on/off instantly in version 16.0.0 and 16.0.1 [/+] on Jul 22, 2026
  14. DedeHai commented on Jul 24, 2026

    @DedeHai
    Collaborator

    #5729 may fix it once I am done with it, I have more changes I still need to test before committing but got pulled away from that by the V5 merge

  15. MACCAMCGOO commented on Aug 19, 2026

    @MACCAMCGOO

    Just to add something to this issue... which may or may not be relevant, but I have observed when I try to apply Swipe left or right to a 16.0.1 set up with multiple segments it does not work, but yesterday I threw together a very simple set up for a light behind my PC with a single segment and the swipes all worked.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions