WLED pattern porting?

I think it’s been raised elsewhere in the feature enhancements thread, but we really need a render1D(index, X) that respects the pixelmap; otherwise the wiring of many 2D and 3D objects really messes up 1D patterns.

I’ve started pasting this into all the 1D patterns so they’ll look the same across all my 2D objects:

export function render1D(index, x) {
  render(floor(x*pixelCount)); // call the original renderer with a corrected index
}

var matrixWidth = sqrt(pixelCount), matrixHeight = pixelCount / matrixWidth;
export function render2D(index, x, y) {
  row = floor(x*matrixHeight);
  column = floor(y*matrixWidth);
  offset = row * matrixHeight + column;
  render1D(index, offset/pixelCount);
}
1 Like

I found a few differences between the original code formulas and yours, minor but significant in effect, like using a2 instead of a3 (I think that was it)

Here’s a slider heavy version, intended to let someone play with all of the variables until they find an effect they like…

Some changes, including…

  • Using wave(time()) rather than time() to avoid what I found were distracting ‘flash changes’ when something went from 1 to 0… also without millis(), we really don’t have a long ‘t’ to replace it. @wizard, consider this a plea for some way to emulate that, but we also don’t really have anything longer than a 16.16 number, which doesn’t really help us here. millis() is used in the original (and many other patterns) as the driving force, since it’ll grow to 4,294,967,295 (32 unsigned) before it rolls over.

  • Log scale sliders, so picking values below and above 1 was easier.

  • Decoupled the time bases… that’s another change from the original code… in Original, time then time/2 then time/3, but in your code above, you grew the time base by x2 and x3

Distortion Waves TNG, with sliders upon sliders
// original code by ldirko_Yaroslaw Turbin 17-06-2021
// https://twitter.com/ldir_ko        https://vk.com/ldirko
// https://www.youtube.com/c/ldirldir https://www.reddit.com/user/ldirko/  

// original code: https://editor.soulmatelights.com/gallery/1089-distorsion-waves
// and https://wokwi.com/arduino/projects/301639284294681097

// modified/recoded by @pixie at https://forum.electromage.com/t/wled-pattern-porting/1295/22
// then re-modified quite a bit by Scruffynerf

//  simple replacement for original LUTs
function cos_wave(proportion) { return 1-wave(proportion+0.25); }
function beatsin(bpm) { return wave(time(0.91552734375/bpm)); }

// adjustments - all sliders now
export var timeBase1 = 0.2;
export var timeBase2 = 0.1;
export var timeBase3 = 0.66;
export var speed = 4;
export var w = .05;
export var scalefactor = .5;
export var coeff1,coeff2,coeff3

export function sliderSpeed(v){
  speed = v*9.9
}

var minv = log(.1);
var maxv = log(3);

export function sliderScale(v){
  scalefactor = exp(minv + (maxv-minv)*v);
}

var minv2 = log(.01);
export function sliderWiggle(v){
  w = exp(minv2 + (maxv-minv2)*v);
}

var maxv2 = log(30);

export function sliderTime1(v){
  timeBase1 = exp(minv2 + (maxv2-minv2)*v) +.0001
}
export function sliderTime2(v){
  timeBase2 = exp(minv2 + (maxv2-minv2)*v) +.0001
}
export function sliderTime3(v){
  timeBase3 = exp(minv2 + (maxv2-minv2)*v) +.0001
}

coeff1 = 1; 
coeff2 = 1; 
coeff3 = 1; 

export function sliderC1(v){
  coeff1 = exp(minv + (maxv-minv)*v);
}
export function sliderC2(v){
  coeff2 = exp(minv + (maxv-minv)*v);
}
export function sliderC3(v){
  coeff3 = exp(minv + (maxv-minv)*v);
}

export function beforeRender(delta) {
  resetTransform()
  //translate(-.5, -.5)
  scale(scalefactor,scalefactor)
  
  a1=wave(time(timeBase1*2))*2-1;
  a2=wave(time(timeBase2*2))*2-1; 
  a3=wave(time(timeBase3*2))*2-1;

  cx1 = beatsin(10-speed)
  cx2 = beatsin(13-speed)
  cx3 = beatsin(17-speed)
  cy1 = beatsin(12-speed)
  cy2 = beatsin(15-speed)
  cy3 = beatsin(14-speed)
}

// debugging uncomment - 
// export var _r,_g,_b,cx1,cx2,cx3,cy1,cy2,cy3,dx1,dx2,dx3,dy1,dy2,dy3

export function render2D(index, x, y) {
  r1 = coeff1*cos_wave(x+a1); 
  g1 = coeff1*cos_wave(x-a2); 
  b1 = coeff1*cos_wave(x+a3);
  
  r2 = coeff2*cos_wave(y-a2); 
  g2 = coeff2*cos_wave(y+a3); 
  b2 = coeff2*cos_wave(y-a1);
  
  rdistort = coeff3*cos_wave(r1+r2+a3); 
  gdistort = coeff3*cos_wave(g1+g2+a1+1/8);  
  bdistort = coeff3*cos_wave(b1+b2+a2+1/4); 

  dx1 = (x-cx1)*(x-cx1);
  dx2 = (x-cx2)*(x-cx2);
  dx3 = (x-cx3)*(x-cx3);
  dy1 = (y-cy1)*(y-cy1);
  dy2 = (y-cy2)*(y-cy2);
  dy3 = (y-cy3)*(y-cy3);

  _r = rdistort + w*(a1-(dx1 + dy1))
  _g = gdistort + w*(a2-(dx2 + dy2))
  _b = bdistort + w*(a3-(dx3 + dy3))
  rgb(_r*_r, _g*_g, _b*_b);
}
1 Like

For a substitute tick count, here’s what I use when porting shader things:

var timebase = 0;

export function beforeRender(delta) {
  timebase = (timebase + delta/1000) % 3600
}

This gives a fractional second counter that rolls over once an hour. Long enough to avoid most visual artifacts, short enough to give you a little room to scale it and not run into integer scaling issues.

2 Likes

I just came across Aurora, and the video of the many patterns it did/does:

Kudos to @JasonCoon and @embedded-creations on all that work.

I’ve been following links and realizing that bitrot is starting to eat older led libraries/demos/etc (led effects by ratkins is long gone, for example, unless I can find a backup repo) so porting is also a form of preservation and carrying algos forward to the future.

Also a port of it to neomatrix:

I think a lot of these still don’t exist for PB yet, so they go on the (growing) porting pile.

3 Likes

Here’s another port in case it’s useful:

Also some more links with the latest details on actually compiling Aurora for SmartMatrix Library (it’s not straightforward as the latest code is on a branch):

3 Likes

If anyone asks, “Why Pixelblaze?” this is one really good answer…

I was wandering through my pattern library, and stopped to look at the code for @pixie and @scruffynerf’s Distortion Waves port. Just casually wondered, “What would this look like with radial coordinates?”

One line of code:
tmp = atan2(y,x); y = hypot(x,y); x = tmp;
added at the front of render - and a few seconds of parameter tweaking later, I had my answer. It’s… really cool! (I’ve attached source below. Absolutely worth running to have a look! )

Yes, you could do this sort of playing and exploration on a compiled platform, but there are a lot more obstacles in the way. Pixelblaze is really excellent at providing the shortest path between crazy idea and actual blinking LEDs!

WLED Distortion Waves - Radial Version
// original code by ldirko_Yaroslaw Turbin 17-06-2021
// https://twitter.com/ldir_ko        https://vk.com/ldirko
// https://www.youtube.com/c/ldirldir https://www.reddit.com/user/ldirko/  

// original code: https://editor.soulmatelights.com/gallery/1089-distorsion-waves
// and https://wokwi.com/arduino/projects/301639284294681097

// modified/recoded by @pixie at https://forum.electromage.com/t/wled-pattern-porting/1295/22
// then re-modified quite a bit by Scruffynerf

//  simple replacement for original LUTs
function cos_wave(proportion) { return 1-wave(proportion+0.25); }
function beatsin(bpm) { return wave(time(0.91552734375/bpm)); }

// adjustments - all sliders now
export var timeBase1 = 0.06228;
export var timeBase2 = 0.04;
export var timeBase3 = 0.057;
export var speed = 9.2;
export var w = .015;
export var scalefactor = 1;
export var coeff1,coeff2,coeff3

var minv = log(.1);
var maxv = log(3);
var maxv2 = log(30);
coeff1 = .21; 
coeff2 = .6626; 
coeff3 = .8245;
var minv2 = log(.01);

/*  uncomment to re-enable UI
export function sliderSpeed(v){
  speed = v*9.9
}

var minv2 = log(.01);
export function sliderWiggle(v){
  w = exp(minv2 + (maxv-minv2)*v);
}

export function sliderTime1(v){
  timeBase1 = exp(minv2 + (maxv2-minv2)*v) +.0001
}
export function sliderTime2(v){
  timeBase2 = exp(minv2 + (maxv2-minv2)*v) +.0001
}
export function sliderTime3(v){
  timeBase3 = exp(minv2 + (maxv2-minv2)*v) +.0001
}

export function sliderC1(v){
  coeff1 = exp(minv + (maxv-minv)*v);
}
export function sliderC2(v){
  coeff2 = exp(minv + (maxv-minv)*v);
}
export function sliderC3(v){
  coeff3 = exp(minv + (maxv-minv)*v);
}
*/

//  resetTransform()
  translate(-.5, -.5)
  scale(scalefactor,scalefactor)

export function beforeRender(delta) {

  a1=wave(time(timeBase1*2))*2-1;
  a2=wave(time(timeBase2*2))*2-1; 
  a3=wave(time(timeBase3*2))*2-1;

  cx1 = beatsin(10-speed)
  cx2 = beatsin(13-speed)
  cx3 = beatsin(17-speed)
  cy1 = beatsin(12-speed)
  cy2 = beatsin(15-speed)
  cy3 = beatsin(14-speed)
}

// debugging uncomment - 
// export var _r,_g,_b,cx1,cx2,cx3,cy1,cy2,cy3,dx1,dx2,dx3,dy1,dy2,dy3

export function render2D(index, x, y) {
  tmp = atan2(y,x); y = hypot(x,y); x = tmp;
  r1 = coeff1*cos_wave(x+a1); 
  g1 = coeff1*cos_wave(x-a2); 
  b1 = coeff1*cos_wave(x+a3);
  
  r2 = coeff2*cos_wave(y-a2); 
  g2 = coeff2*cos_wave(y+a3); 
  b2 = coeff2*cos_wave(y-a1);
  
  rdistort = coeff3*cos_wave(r1+r2+a3); 
  gdistort = coeff3*cos_wave(g1+g2+a1+1/8);  
  bdistort = coeff3*cos_wave(b1+b2+a2+1/4); 

  dx1 = (x-cx1)*(x-cx1);
  dx2 = (x-cx2)*(x-cx2);
  dx3 = (x-cx3)*(x-cx3);
  dy1 = (y-cy1)*(y-cy1);
  dy2 = (y-cy2)*(y-cy2);
  dy3 = (y-cy3)*(y-cy3);

  _r = rdistort + w*(a1-(dx1 + dy1))
  _g = gdistort + w*(a2-(dx2 + dy2))
  _b = bdistort + w*(a3-(dx3 + dy3))
  rgb(_r*_r, _g*_g, _b*_b);
}
1 Like

actually @zranger1, it’s funny you mention this, cause… aircoookie of WLED is doing a survey, specifically asking about what you’d like to see in a “Live effect editor”

As you may or may not know, I am currently working on my Bachelor’s thesis, and you can help!
The topic is related to WLED and I am conducting some research to assess how much you would like a specific new feature.

It would delight me if you can spare 5 minutes to answer the survey:
https://forms.gle/bHxcg4N5BkUf2rhY6
This survey will run for 14 days.

Thank you very much and have a nice week!

1 Like

Ha! I filled it out – it’ll be interesting to see what he comes up with. Wonder how many votes, “Give us the option of switching between polar and cartesian coordinates”, will get.

1 Like

Now that we’ll have array literals, a few things become easier. Palettes for one thing.
@jeff 's palette utility used some nifty functions to ‘set3’ or ‘set8’ to populate arrays.
Now that it’ll be much easier to just put the array in directly, the code can be cleaner.
I need to revisit it, and see how easy it would be use it for ‘quick’ porting purposes from fastled/wled.

1 Like

Ok, I’ve got all of the main wled palettes ported now. Code shortly. I want to do a lot of cleanup on it. I’ve taken @pixie 's code, and added 8bit packing, so each palette is now essentially a one dimensional array of single 16.16 numbers, stored as small as is possible, meaning you can pack quite a bit in there.
The library will include all of the wled palettes but limiting your own code to a handful you that like will be pretty easy.

It’s not perfect yet… still some bugs to find.
for example:

wledpalette[0] = [
  p( 0, 194, 1, 1),
  p( 94, 1, 29, 18),
  p(132, 57, 131, 28),
  p(255, 113, 1, 1)
];

when I watch that, I get:
wledpalette[0] = [1.006866, 24093.07, -31612.89, -254.9944]
but that’s not quite the same pattern, missing some blue. I suspect it’s 16.15 issue, (but I’m storing red not blue in the last bits… ) but… we’ll see.

1 Like

TIL: WLED has a fixed FPS (frames per second) of 42.

So given how simple some of the patterns are, some sort of FPS limiting will likely be needed, as I’m pretty sure they’d run faster than 42 FPS on a PB.

I did just outline a way to do “delays” in PB, but I suspect matching a FPS like this is a bit more complex. It would involve looking at the Delta and ensure it stayed in the right range (23.8095238 milliseconds per render - so between 23 and 24 millis of Delta) and then do something to either speed up (skip a frame?) or slow down (stay static for a frame?). Ideas?

@Scruffynerf ,
I’ve been considering adding a skipRender() API that could be called in beforeRender. Then you could just skip frames and return early from beforeRender until it was time to render a frame (23.8ms has passed).

However, wouldn’t it be much nicer if the patterns were modified to scale with frame rate?

If your target was 23.8ms per frame, you can get a scaling factor to apply for any changes like this:

var scale42Factor = 1
export function beforeRender(delta) {
  scale42Factor = delta / 23.8095238
  
}

Then you can multiply scale42Factor to any value that changes per frame. It would be pattern by pattern though. Most of them are ugly / boring though. Might be better to just chip away at them one at a time looking at the most interesting ones, and factor out any framework / helpers that you discover along the way.

2 Likes

FastLED/WLED have the EVERY_N_MILLISECONDS() idiom to perform actions at a consistent rate no matter what the actual framerate is, which is achievable (but less elegant) by accumulating delta inside the beforeRender() function like so:

//  FASTLED emulation functions
var maxTimers = 10;
 var elapsedTime = array(maxTimers);
function EVERY_N_MILLISECONDS(delta, accumulator, target, func) {
  elapsedTime[accumulator] += delta;
  if (elapsedTime[accumulator] > target) { 
    elapsedTime[accumulator] -= delta; 
    func(); 
  }
}

//  Simple sample pattern:
export var hue = 0; 
export var brightness = 0;
function changeHue() { hue = (hue + 1/6) % 1; }
function changeBrightness() { brightness = (brightness + 1/10) % 1; }

export function beforeRender(delta) {
  EVERY_N_MILLISECONDS(delta, 0, 500, changeHue);
  EVERY_N_MILLISECONDS(delta, 1, 50, changeBrightness);
}

export function render(index) { 
  hsv(hue, 1, brightness);
}

But @wizard, having a skipRender() to avoid unnecessary calculations and outputs could be very helpful for reducing power in mobile/portable installations…

Actually, from the same Reddit post I learned about the 42 fps (by @Johnny5canuck, who I’m tagging here):

That being said, there’s additional learning required in order to work within the WLED and NeoPixelBus framework. For instance, you cannot use EVERY_N_MILLIS or the ‘static’ keyword and be fully WLED compatible. Those do not work with segments. It also runs at a fixed 42 fps.

So while yes, FastLED has that, WLED patterns aren’t supposed to, but perhaps some do? (And thus don’t work with segments)

I do agree with @wizard, that handling the FPS limit on a case by case basis makes sense. I mostly raised it as an interesting data point. I suspect the “port all the things” end result will be a handful of PB patterns, rather than a monolithic “WLED effects” pattern, and breaking them up into different types (like all of the simple 1D Marquee/alternator ones, especially the fixed colors like Halloween or Christmas, as one pattern with selectable colors/combos.) Hopefully, we’ll have a new playlist to support running the same pattern with different variable presets for some day… Otherwise that “bundling” would be (mildly) annoying if you wanted to run two or more of the effects, as you’d have to clone the pattern each time.

Not mention implementing segments, other than our current “multisegment” pattern method.

Thoughts:

The JavaScript idiomatic way of doing EVERY_N_MILLISECONDS() is to use an interval (which doesn’t exist in PB yet):

setInterval(function() {
  // do stuff here
}, 500) //every 500 ms

One problem here is that using a unit of milliseconds limits the range to about 32 seconds due to the fixed point value range in PB. It would be useful even with that limit, and a variant that worked in seconds would come in handy. I don’t think that would be a problem for these cases.

It also calls the timeout function asynchronously (in the main event loop after other code has returned), whereas the EVERY_N_MILLISECONDS() macro executes it in-line. I suspect for most things the timing wouldn’t matter.

Here’s an implementation of setTimeout (in seconds), which is the one-shot version. It’s not too dissimilar to your implementation, @pixie.

Just adding my 2cents that it would be great to port the WLED patterns to PB. While many are similar there are some cool ones in the WLED library. The Pacific one was a classic (ported to PB as Oasis).

I’ve got both PB3 … and WLED12 / WLED Sound Reactive running on ESP32, and I’m trying to pick out the more uninque WLED effects to find the ones we don’t have great examples of. The WLED Sound Reactive port has some great sound-reactive patterns that we don’t seem to have yet… https://github.com/atuline/WLED/wiki/Reactive-Animations

We really need a better pattern repo (electromage) that supports tags and more sorting options so we can sort the existing PB pattern library better… hows that going?

I believe the next version is more GitHub repo based, but I like the idea of a tag, which would allow potential multiple ways to sort… So a pattern might be “1D”, “2D”, “sound reactive”, etc.

1 Like

Yep, the more descriptions and sort fields the better I say.

For example I’m often looking for patterns that are more ‘generative’ or ‘long-form’, or ‘story-like’… not sure how to describe it exactly, but I guess all patterns are ‘loops’, but some seem to be more free-form, or at least long-form… it would be great to describe/tag patterns in terms of ‘temporal-complexity’ (ie. how long before the pattern loops).

1 Like

Just for reference in this thread… WLED is developing pattern previews with 2D GIFs … https://github.com/scottrbailey/WLED-Utils/blob/main/effects_sr.md

1 Like

To add my 2 cents, i’ve tried Wled because i need to use a smaller controller than Pixelblaze, and i’ve been so disapointed by the patterns (particularly sound reactive ones) i’m considering hacking the PB Pico to handle a microphone.

Honestly Wled is way above in terms of app ergnonomics and a few setup options (like Artnet), but the patterns are pathetic compared to Pixelblaze. It looks so cheap. So while i have nothing against porting patterns to PB, i believe creating our own ones and facilitating the integration into an app would be a much better use of the community’s time.

Wled is a fantastic piece of software and the work behind is massive, don’t get me wrong though.