Input needed: functions wishlist

Maybe it’s bad practice but I just bit that all up top outside of any other function. Runs once!

1 Like

Same. It works fine. That’s one of the advantages of PB.

Right, init/setup code is the code in the main body of the pattern “module,” and is run once when the pattern loads. Even if you don’t write a bunch of code there, just the code setting up exported variables and functions is still being run.

That is different than in a C code file where you generally can’t write arbitrary code outside of a function in the outer file area, and only declarations are possible (which still might evaluate expressions and cause code to run though).

It’s normal for JavaScript modules to do some basic module setup in the module’s body of code, even if it’s just to import/export and set up a function or variable. JavaScript / ES6 module system gave the core language a way to handle module dependencies and sharing and to ensure that things are initialized in the right order based on those dependencies. Some frameworks might give you a hook for initialization, but that is framework dependent.

Arduino popularized the setup() + loop() framework because hitting a beginner with this boilerplate C code is counterproductive:

#include <stdio.h>
int main(void) {
  //put your setup code here
  for(;;) {
    //put your main loop code here
  }
}

By the way, render and beforeRender etc are just variables, like other exports. They just happen to be set to functions while the module code is run and usually only point to a single function definition.

Here is a trick that I’ve been avoiding using because it can lead to things that are too clever (harder to read/understand):

export var render
var redRender = () => hsv(0,1,1)
var greenRender = () => hsv(.333,1,1)
if (random(1) > .5)
  render = redRender
else
  render = greenRender

In the above pattern, each time it is run the render variable’s value is randomly chosen though the setup/init code in the main pattern body to be one of 2 possible functions. I’ve specifically avoided coding anything like this, preferring to add code in a generic render that dispatches to other functions because I think it could get very confusing.

There are also some possible JavaScript / ES6 ways of exporting that we haven’t used. You can do something like this, though there is little reason to do so now:

var redRender = () => hsv(0,1,1)
export {redRender as render}

Here’s some more export syntax examples.

2 Likes

Rubbing my hands and cackling wildly at the new insights based on realizing this exists.

Thing that I found is that you can’t call a function before it’s been defined (no forward declarations).

Also once everything is loaded, the loading mechanism then calls all the slider functions to set them to the preset values, so if you call a function in a slider definition, the initial code runs, then the functions defined in slider functions run - all of them but I don’t know in what order.

But you’re right, you don’t need a setup() function.

Don’t forget that a slider only loads the previous value IF it’s been saved by running previously. On first run, the slider won’t do that.

@wizard , I know it’s on the bottom of the editing page on the PB, but could you update the website expression page?

Pixelblaze Expression Docs — Ben Hencke is still lacking all of v3 changes like array functions, and so on.

I find myself using that page more often when I’m online as it’s handy.

@Scruffynerf
Thanks for the heads up. I had forgot that was there! I’ve updated the page with links to the GitHub source docs.

I think Smooth Speed Slider is a good candidate for a built-in function. Many PBers will encounter the problem early, and be confused at first. The subtlety of the smooth solutions in that thread might be over the average head. I had pondered the ‘maintaining phase’ aspect but was glad to find it solved for me!

1 Like

@sorceror ,
Yes, that has been discussed before as well, and is on the list.

Goals:

  1. A global speed slider like brightness
  2. Changing speed without causing animation jumps
  3. Not breaking timesync across multiple PB - sync the global speed too.
  4. New stateful API, perhaps a timer object, to keep track of internal phase state.
  5. Both timer adjustable speed and global speed so you can add more speed sliders in addition to the global one to fine tune some particular aspect.

Not really a function, but … I was thinking that it would be neat if there was a way to retain values between pattern changes. So for example, to keep a time-slider-controlled wave smooth, or to set a “theme color” which applies to every pattern in a playlist.

Maybe something like:

 export persistent var foo;

which stores foo’s value in a metaglobal hash table when leaving a pattern, and restores it after the next pattern is compiled.

2 Likes

At first, I didn’t understand, I was like “but pattern vars do persist, if you leave and come back…”

but you meant a shared var for multiple patterns.

Yeah, that would be interesting, a ‘superglobal’.

Shared globals would be very useful, but shared functions would be far more useful still. I have lots of ideas for utility functions that could be reused across patterns, and currently the idea of having to cut and paste them (and then maintain all those copies) puts me off creating them. Perhaps a separate editor page that could contain code that is automatically included in every pattern would be an easy way to implement this? Though I can see that being problematic in some situations, e.g. for sharing patterns between users.

1 Like

An “include” would solve all of that… Including libraries, global stuff, etc.

include("coordinates")
include("myglobalslider")

2 Likes

Yes, “include” would be a great way to do that, but automagically persisting included values (e.g. of same-named sliders) between patterns might lead to unexpected results. I’d rather be intentional about that with a keyword or by storing such things in a global hash table.

“include” also puts more demands on the internal file system. I’m willing to forego installable libraries 'till we have a little more storage and CPU time for file I/O.

export persistent is an interesting idea. I’d like to have it as a feature.

It’s got a lot of potential implementation pitfalls though. How many variables should I be able to export? How do I handle namespace conflicts? Can I “export persistent” an allocated array? (probably not) If so, how large? How about a single variable containing a pointer? (No!) An array of pointers to functions? (NOOOOOO!!!)

1 Like

I don’t see why that’s true. If anything, having include would reduce pattern size, since redundant code would be included, instead of repeated in each pattern. Keeping in mind that PB uses byte code to store binary programs, even though it’s got an editor that allows easy on the fly editing, it’s really not using the text format except as human readable editing and recompiled.

In this case, include “pattern name” would essentially grab the code from that pattern as if it was typed directly. And treat it as if it was there.

Seems much simpler than worrying about the internal file system.

I think we have all voted for some sort of library function. I have a ton of boilerplate code that just gets pasted into every pattern, and whenever I update it, I have to copy and paste every pattern with the updated version…

I would like to vote for literal arrays. I still struggle not being able to create a look up table. For palettes, corrections, speed vs calculation etc.

2 Likes

Wow, this reply makes 60 comments on this thread and some of the above has been implemented.

Might be worth reviewing, and pulling out what’s done, and what could be done without adding it to the firmware first. (Mappixels being a good example, we’ve got code that can “fake it”, so maybe a working library example and then if it’s really useful, it can be done in firmware [and faster]? )