Skip to content

New transitions also for individual segments. #5520

Description

@patryk84a

Good morning. The new features in version 16 are great. I especially like the new transitions when turning bars on and off. If we have a single strip and turn it on and off globally, the transitions work great. However, if the bar is divided into segments and we only want to turn a single segment on and off, the transitions don't work. I'm not looking for a workaround using presets, because in my case, when I want to control two segments with a single button (single-click and double-click), presets won't work. It would be nice if the transitions also worked when turning a single segment on and off.

Activity

  1. DedeHai commented on Apr 21, 2026

    @DedeHai
    Collaborator

    @coderabbitai please check if this is feasible with current code structure. Make a list of required changes to enable transitions on a single segment as requested.

  2. coderabbitai commented on Apr 21, 2026

    @coderabbitai
    Contributor

    Coding Plan

    Summary

    • The per-segment transition system (Segment::startTransition(), Segment::currentBri(), handleTransition()) already fully supports on/off fading
    • The bug is that the global handleTransitions() machinery terminates per-segment transitions when its timer expires, even when no global brightness change occurred
    • The fix is minimal: only activate the global transition timer (transitionActive = true) when bri != briOld
    • Per-segment transitions will then run autonomously, cleaning themselves up when their individual _t->_dur expires
    Design Choices
    Design Choice 1: Should per-segment transitions be independent of global transition timing?

    Options Considered:

    1. Keep segment transitions coupled to global timer (current broken behavior)
    2. Make segment transitions fully autonomous when no global brightness change occurs

    Chosen Option: 2

    Rationale: Per-segment transitions should run autonomously when there's no global brightness change, as this allows independent segment control without interference.

    💡 User Tips

    Regenerate the plan with different choices with @coderabbitai <feedback>.

    Implementation Steps

    Decouple Per-Segment Transitions from Global Timer

    The root cause is that stateUpdated() unconditionally activates the global transition timer (transitionActive = true) whenever any state change occurs, even for segment-only changes. When this timer expires, handleTransitions() calls strip.setTransitionMode(false), which terminates ALL per-segment transitions by setting their _t->_dur = 0. The per-segment transition infrastructure already works correctly—it just gets killed prematurely.

    Task 1: Modify Global Transition Activation Logic

    Modify the transition activation in stateUpdated() to only start the global brightness interpolation timer when there's an actual global brightness change.

    • In wled00/led.cpp, locate the stateUpdated() function (around line 87)
    • Find the block that unconditionally sets transitionActive = true and transitionStartTime = now (around lines 133-136)
    • Wrap these two lines in a conditional if (bri != briOld) check
    • This ensures the global timer only runs when briT actually needs to interpolate between briOld and bri
    • When only segment state changes (stateChanged is true but bri == briOld), per-segment transitions will manage themselves via their own _t->_dur and handleTransition() calls in service()
    Task 2: Handle One-Time Transition Duration Cleanup

    Ensure the jsonTransitionOnce flag is properly reset for segment-only changes, since the global handleTransitions() cleanup path will no longer run.

    • In the same stateUpdated() function in wled00/led.cpp
    • Add an else if (jsonTransitionOnce) branch after the if (bri != briOld) block
    • In this branch, reset the transition duration via strip.setTransition(transitionDelay) and set jsonTransitionOnce = false
    • This cleanup can safely occur after setTransitionMode(true) because per-segment transitions have already captured their duration in _t->_dur during startTransition()
    • The ordering is important: setTransitionMode(true) runs first and starts segment transitions with the one-time duration, then we reset to default for future requests
    🤖 Prompt for AI agents
    Modify `wled00/led.cpp` in the `stateUpdated()` function (around line 87) to
    decouple per-segment transitions from the global transition timer, ensuring the
    global timer only activates on actual brightness changes.
    
    - Locate the block around lines 133-136 that unconditionally sets
    `transitionActive = true` and `transitionStartTime = now`
    - Wrap those two lines in a conditional `if (bri != briOld)` check, so the
    global timer only runs when `briT` needs to interpolate between `briOld` and
    `bri`
    - After the `if (bri != briOld)` block, add an `else if (jsonTransitionOnce)`
    branch that:
    - Calls `strip.setTransition(transitionDelay)` to reset the transition duration
    back to default
      - Sets `jsonTransitionOnce = false`
    - The `else if` branch must come after `setTransitionMode(true)` so that
    per-segment transitions have already captured their one-time duration in
    `_t->_dur` via `startTransition()` before the reset occurs
    
    Research

    WLED uses a dual transition system: a global brightness interpolation mechanism in led.cpp (handleTransitions()) that manages hardware-level brightness fading, and a per-segment transition system in FX_fcn.cpp (Segment::startTransition(), Segment::currentBri()) that handles segment-level opacity blending. The per-segment system is already fully capable of handling on/off fades through the Transition struct and currentBri() interpolation. The issue is that the global machinery prematurely terminates per-segment transitions even when no global brightness change occurred.


    🚀 Next Steps

    🤖 All AI agent prompts combined
    Modify `wled00/led.cpp` in the `stateUpdated()` function (around line 87) to
    decouple per-segment transitions from the global transition timer, ensuring the
    global timer only activates on actual brightness changes.
    
    - Locate the block around lines 133-136 that unconditionally sets
    `transitionActive = true` and `transitionStartTime = now`
    - Wrap those two lines in a conditional `if (bri != briOld)` check, so the
    global timer only runs when `briT` needs to interpolate between `briOld` and
    `bri`
    - After the `if (bri != briOld)` block, add an `else if (jsonTransitionOnce)`
    branch that:
    - Calls `strip.setTransition(transitionDelay)` to reset the transition duration
    back to default
      - Sets `jsonTransitionOnce = false`
    - The `else if` branch must come after `setTransitionMode(true)` so that
    per-segment transitions have already captured their one-time duration in
    `_t->_dur` via `startTransition()` before the reset occurs
    
    💡 Iterate on the plan with: @coderabbitai <feedback>
    Example Feedback
    - `@coderabbitai` You can skip phase 3. Add a simple unit test case for phase 2.
    - `@coderabbitai` For design choice 1 go ahead with option 3 and replan.
    

    💬 Have feedback or questions? Drop into our discord!

  3. blazoncek commented on Apr 21, 2026

    @blazoncek
    Contributor

    As I wrote transitions and blending I'm curious to know what you mean by "not working".
    Can you post a video of the behaviour?

  4. patryk84a commented on Apr 21, 2026

    @patryk84a
    Author

    To put it simply, when I use this button (marked in red) to turn the entire strip on/off, the transition works when the LED strip starts and stops lighting up.

    Image

    https://drive.google.com/file/d/1kiWBEBiYTNCYV81jlx9Mt8ie0Trp_GFW

    However, when I use these buttons to turn individual segments on and off, the transition effect doesn't work. The LED strips turn on and off immediately without a transition.

    Image

    https://drive.google.com/file/d/1xTWpBvqfos8Ml_ajJtrpWJmjA9M3NUjK

    I control it from an external device, a smart home controller, via the WLED API. I want these two LED strips to light up separately, but with a transition effect. One strip lights up and down with a single click (toggle), and the other strip lights up and down with a double click (also toggle).

    While playing around with and changing the transition effect, I discovered that when I select the "fade" transition, it works when turning a single segment off and on, while the other effects do not work.

    Image

    https://drive.google.com/file/d/1s6-FfqdcaKKZX0ACs4xRgHR_XGDdpHB4

    If Fade works but other effects don't, it might not be a lot of work to fix.

  5. blazoncek commented on Apr 22, 2026

    @blazoncek
    Contributor

    @DedeHai rabbit is partially (if not entirely) right. Follow its path.

  6. DedeHai commented on Apr 22, 2026

    @DedeHai
    Collaborator

    thanks for the confirmation. I tested the rabbits suggestion and while it works for opacity changes, it does not solve the transitions in general. I have some more changes that make it work but there are still some issues, like changing global brightness during a opacity transition yields wrong brightness and jumps. Once I am happy with the result, I will do a PR for review.

  7. blazoncek commented on Apr 22, 2026

    @blazoncek
    Contributor

    It is a bit more complicated than that. The main nuisance is legacy code in led.cpp.

    Changing anything during an existing transition will have abrupt change except if you take the convoluted path.

  8. DedeHai commented on Apr 22, 2026

    @DedeHai
    Collaborator

    @patryk84a please test #5524

  9. patryk84a commented on Apr 22, 2026

    @patryk84a
    Author

    Thanks for the work. I'll compile and test it tomorrow.

  10. patryk84a commented on Apr 23, 2026

    @patryk84a
    Author

    @DedeHai I tested it, and it works as expected. Thanks!

  11. blazoncek commented on Apr 23, 2026

    @blazoncek
    Contributor

    it works as expected.

    IMO not for all possible combinations. It does, however, for your particular needs.
    I encourage you to test also other scenarios that don't involve On/Off.

  12. patryk84a commented on Apr 23, 2026

    @patryk84a
    Author

    The color transitions also work well. However, I've noticed that there's a slight fade in and fade out when turning the main button on and off. There's no fade when turning individual segments on and off. But it's barely noticeable, and in my opinion, the fade in isn't necessary. Is it permanently programmed? But this is a minor detail, almost invisible.

  13. added this to the 16.1 milestone on Apr 28, 2026
  14. blazoncek commented on May 5, 2026

    @blazoncek
    Contributor

    I managed to track the root cause of the issue and the solution is one-liner (actually 2, one for 1D and one for 2D).
    There is at least 1 other bug also present (mentioned above) which I also solved in my fork. That one needs more profound changes.

  15. andreas-ztr91 commented on Jul 19, 2026

    @andreas-ztr91

    I wish I read this sooner. Spent the last 3 days testing and documenting this exact usecase on multiple boards (ESP8266 and ESP32) and multiple segments/strips, on 16.0.1. Exactly the same, calling the API to turn segments on/off with a transition set. Every transition style, except fade, snaps on/off instantly when scoped to a segment, but animates correctly when sent as a whole device toggle.
    May I ask if this is still actively being worked on? Is it expected to be part of 16.1?
    Thanks guys, as always, really appreciate the work you put into this.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions