Repository navigation
New transitions also for individual segments. #5520
Description
Activity
@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.
Reacted by coderabbitaicoderabbitai commented
on Apr 21, 2026 coderabbitaiboton Apr 21, 2026 – with coderabbitaiContributorMore actionsCoding 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) whenbri != briOld - Per-segment transitions will then run autonomously, cleaning themselves up when their individual
_t->_durexpires
Design Choices
Design Choice 1: Should per-segment transitions be independent of global transition timing?
Options Considered:
- Keep segment transitions coupled to global timer (current broken behavior)
- 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()callsstrip.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 thestateUpdated()function (around line 87) - Find the block that unconditionally sets
transitionActive = trueandtransitionStartTime = now(around lines 133-136) - Wrap these two lines in a conditional
if (bri != briOld)check - This ensures the global timer only runs when
briTactually needs to interpolate betweenbriOldandbri - When only segment state changes (
stateChangedis true butbri == briOld), per-segment transitions will manage themselves via their own_t->_durandhandleTransition()calls inservice()
Task 2: Handle One-Time Transition Duration Cleanup
Ensure the
jsonTransitionOnceflag is properly reset for segment-only changes, since the globalhandleTransitions()cleanup path will no longer run.- In the same
stateUpdated()function inwled00/led.cpp - Add an
else if (jsonTransitionOnce)branch after theif (bri != briOld)block - In this branch, reset the transition duration via
strip.setTransition(transitionDelay)and setjsonTransitionOnce = false - This cleanup can safely occur after
setTransitionMode(true)because per-segment transitions have already captured their duration in_t->_durduringstartTransition() - 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 occursResearch
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 inFX_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 theTransitionstruct andcurrentBri()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!
- The per-segment transition system (
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?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.
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.
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.
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.
@DedeHai rabbit is partially (if not entirely) right. Follow its path.
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.
Reacted by Will TatamIt 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.
@patryk84a please test #5524
Thanks for the work. I'll compile and test it tomorrow.
@DedeHai I tested it, and it works as expected. Thanks!
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.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.
- linked a pull request that will close this issuemake per-segment on/off and brightness behave the same as global #5524
on Apr 28, 2026 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.Reacted by Damian SchneiderI 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.
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.