Input needed: functions wishlist - Part 2

I have a text file with my most commonly used copy-paste blocks! Happy to share the actual code too.

  • Perceptual hsv
  • near(a, b, distance) Return a value in 0…1 for how close these values are. I use this a surprising amount. I also have a wrappedNear(a, b, angle) for angles or hue.
  • Code that allows changes to the time() parameter to have continuous output (but discontinuous first derivative) when the parameter changes
  • Fade in/out through black with a fade time and total duration
  • Twinklers
  • Tweens
  • Compile statistics, like the running average
  • Scale map dimensions for use with other PB and Firestorm

Not all these might be right for libraries or built-ins. But that’s what I most commonly copy-paste!

4 Likes

Yes to fade! Forgot that. I use FastLed quite a bit, and I forgot how much I use fade.

1 Like

Please do, even if it’s just in a GitHub repo

Switch function, like the slider function - but gives 1 or 0.

export var on
export function switchON(v) {
    on = (v==1)
}

Global on/off control, exposed via websocket (like global brightness).

When set to “off” writes 0’s for the number of pixels in the strip, then stops writing. when turned on, resumes current pattern.

  • array.includes()
  • array.indexOf()

I use this to blank out specific pixels on a strip (put the number of the pixel in the array, and check it in render()).

3 Likes

A slider with fixed positions would work.

So 2 position is on off
3 position might be -1, 0, 1
Labels would be the most important addition.

I would like to add a trigger and a toggle.

A toggle for an on/off switch.

Screen Shot 2021-04-30 at 2.56.15 PM

A trigger is unlike other controls in that it is stateless and has no value. Triggers would show in the UI as a button.

e.g.

export function triggerNewSpark() {
 newSpark();
}
export function toggleColorMode(isEnabled) {
  colorMode = isEnabled
}

I’d also like to add some basic output controls, like a gauge, bar, or numbers you could watch without needing to visit the vars watcher.

export function gaugeAudioLevel() {
  return energyAverage
}

export function meterLight() {
  return light
}

export function showNumberPixelCount() { //control prefix still TBD
  return pixelCount
}

Screen Shot 2021-04-30 at 3.03.46 PM

Screen Shot 2021-04-30 at 3.05.43 PM

For arrays, maybe some day little simple line/bar charts could be cool too.


Ultimately I’d like to have some kind of tool that would let you wire physical inputs to controls outside of code. So e.g. you could configure a pattern someone wrote that used a slider control to take its input from an analog input instead. Similarly for trigger/toggle controls to a GPIO wired to a button or switch.

It would be much easier than coding up debounce logic and configuring pin numbers in code and hopefully help spread some of the awesome interactive pattern stuff we’ve seen folks create with controls to physical inputs as well.

9 Likes

At risk of losing this in the coming feature thread consolidation, I just want to get this on the record while I’m thinking about it.

Because I find myself coding bits of this in nearly every pattern lately, I’d like to see support for vector data types and simple vector math.

  • These types: vec2 (xy), vec3 (xyz), and vec4 (xyzw). The ability to refer to individual elements by symbolic name would be nice, but index is good enough.
  • Simple constructors to initialize them, something like: vec3 p = vec3(0,1,2)
  • Arithmetic operator and function overloading where appropriate so you can say things like
    vec2 p = somevec2 + anothervec2
    and
    vec3 result = sin(aCoordinateVector) * aScalingVector
  • A few vector-specific functions. Dot product dot() is the only real essential.
    Edit: Oh yeah, length() and normalize() would be good too.
1 Like

Seconded. For complex number handling, I wrote a lot of code to handle the lack of vectors aka using 2 element arrays.

So the short answer is complex number library can do it (it lacks the 3 and 4 dimension stuff only because I didn’t need to write it yet)

But a built in would make code much cleaner.

Actually, @zranger1 , it might be worth considering how much of the webgl API we can emulate/simulate…

https://xem.github.io/articles/webgl-guide.html

One step closer to shaders… Ok, 10 steps closer to shaders.

Maybe a checklist of what webgl does vs PB. Obviously, we can’t do it all, but I wonder if enough can be done (in code first, of course)

Also, Canvas API - Web APIs | MDN

How about an in-browser Git client so we can backup and version-control patterns? If you pull it down from a CDN it won’t take up any flash space…

1 Like

Hmm, add a setting for GitHub username and repo, and you’d be all set. I like that.

…and if I can go further outside the scope of “functions” to suggest some enhancements to the Pattern Library:

  • Filtering/searching for pattern names
  • Batch downloading of the entire pattern library as a .ZIP – else how can we keep up to date with other peoples’ contributions? Could regenerate the ZIP after each pattern submission and then push it to a CDN like CloudFlare so downloading won’t chew up your site bandwidth.
  • Detect the presence of render{1,2,3}D() functions and display badges for 1D/2D/3D/accelerometer/audio on each listing so we don’t need to put it into the name
  • Allow the original submitter to update the pattern code; also update the modifiedDate so it shows up as a recent pattern again
1 Like

I believe the next gen of the pattern library is planned to be a git setup, including both .epe and source…

That will help greatly with most of your points.

With the new change that render3D works with 2D patterns (using .5 Z always I believe), your suggestion won’t be accurate if automatic. I’m fine with labels saying 123D and audio (sensor?)

That would be great; at the moment it’s almost impossible to keep straight what’s in the library versus what’s on each PB. With today’s v2 firmware release bringing the language versions up to parity, now I’ve got to export all my patterns off all my 'blazes, convert them to text files and diff them before I’ll know where I need to remove v2 shims and add in arrays and matrices…

To that end, I’ve spent the afternoon knocking together a little applet to export all patterns off all PBs to a folder tree so I can check in a baseline to git; then after I do my language upgrades I can use the commit log to know what I need to re-upload to which PB (sadly, still a manual process).

If anybody wants to do the same, here’s the code:

#!/usr/bin/env python3

import requests, pathlib, json, base64
from pixelblaze import *
from lzstring import LZString


if __name__ == "__main__":
    # create a PixelblazeEnumerator object, listen for a couple of seconds, then list the Pixelblazes we found.
    pbList = PixelblazeEnumerator()
    print("Listening for Pixelblazes...")
    time.sleep(3)

    # Make a top-level folder to hold the backups.
    pathlib.Path('./Backups').mkdir(parents=True, exist_ok=True)

    for pixelblazeIP in pbList.getPixelblazeList():

        # create a Pixelblaze object.
        print("Connecting to Pixelblaze @", pixelblazeIP)
        pb = Pixelblaze(pixelblazeIP)
        pb.stopSequencer() # so the patterns don't change while we're copying them
        hardwareConfig = pb.getHardwareConfig()
        pixelblazeName = hardwareConfig['name']
        print("  Connected to ", pixelblazeName)
        time.sleep(1)

        # Make a subfolder for each Pixelblaze.
        devicePath = './Backups/' + pixelblazeName
        pathlib.Path(devicePath).mkdir(parents=True, exist_ok=True)

        # Save the hardware characteristics in case we need to rebuild it at a later date.
        hardwareFilename = devicePath + '/deviceSettings.json'
        with open(hardwareFilename, 'w') as outfile:
            json.dump(hardwareConfig, outfile)

        # Make a subfolder for Patterns.
        patternPath = devicePath + '/Patterns'
        pathlib.Path(patternPath).mkdir(parents=True, exist_ok=True)

        # Fetch the patterns and save them..
        print("  Fetching patterns:")
        for patternID, patternName in pb.getPatternList().items():
            # Set the active pattern so we can query it.
            print("    Saving pattern '%s'" % patternName)
            pb.setActivePattern(patternName)
            pb.waitForEmptyQueue(1000)
            time.sleep(1)

            # save the pattern as a binary blob (.BIN)
            patternFilename = patternPath + '/' + patternName.replace("/","_")
            suffix = ''
            url = 'http://' + pixelblazeIP + '/p/' + patternID + suffix
            r = requests.get(url)
            binaryData = r.content
            open(patternFilename + '.bin', 'wb').write(binaryData)

            # also save the pattern as a portable JSON archive (.EPE)
            header_size = 36
            offsets = struct.unpack('<9I', binaryData[:header_size])
            name_offset = offsets[1]
            name_length = offsets[2]
            jpeg_offset = offsets[3]
            jpeg_length = offsets[4]
            source_offset = offsets[7]
            source_length = offsets[8]
            epe = {'name': binaryData[name_offset:name_offset+name_length].decode('UTF-8'), 'id': patternID}
            epe['sources'] = json.loads(LZString.decompressFromUint8Array(binaryData[source_offset:source_offset+source_length]))
            epe['preview'] = base64.b64encode(binaryData[jpeg_offset:jpeg_offset+jpeg_length]).decode('UTF-8')

            with open(patternFilename + '.epe', 'w') as outfile:
                outfile.write(json.dumps(epe, indent=2))

            # also save the pattern as text (.JS)
            with open(patternFilename + '.js', 'w') as outfile:
                outfile.write(epe['sources']['main'])

            # save the control states, if any.
            if len(pb.getControls(patternName)) > 0:
                suffix = '.c'
                url = 'http://' + pixelblazeIP + '/p/' + patternID + suffix
                r = requests.get(url)
                open(patternFilename + '.c', 'wb').write(r.content)
                time.sleep(1)

        pb.close()

    print("Complete!")
    exit()

You’ll just need to put it in a folder along with a copy of @zranger1’s pixelblaze-client class and @wizard’s lzstring class.

1 Like

On webgl: I’d completely ignore the vertex shader portion for now. It really complicates the pipeline, and geometry isn’t really that interesting given the low spatial resolution of most LED things. I’d save the full implementation for when we’ve got more CPU and RAM, possibly a dedicated GPU, and everybody’s using generally higher res displays.

The fragment shader however…

What you see on shadertoy is done almost exclusively in fragment shaders. They’re enormously powerful. And Pixelblaze is already nearly identical in functionality. Even the limitations are similar. The only painful bit when porting would be dealing with the vector types, which is partly why I asked for them. ( The other reason is performance – you can do vectors in Pixelblaze code, but it’s exactly the kind of thing that performs marvelously better when done in the VM.)

If you find something that looks good on a low resolution display, are willing to deal with the vectors yourself, and can manage the (fairly 1:1) translation from GLSL C to Pixelblaze js, it’s already possible to port things from shadertoy to Pixelblaze without too much trouble.

2 Likes

I can add vector / matrix APIs. You’d call APIs like vec3_add(dest, v1, v2) or something like that. I could also add helper aliases for making fixed size arrays like v = vec3(1,2,3).

Extending the language operators themselves so that you can do somevec2 + anothervec2 and that sort of thing requires significant upgrades to the language / engine.

Would something like that be interesting?

3 Likes

Absolutely, see the other new thread on object property faking. If we had vectors, properties objects, and classes, done any easier than faked via the current kinda sorta way we can seem to do it now, that would be awesome.

1 Like

I wanted “objects” (not necessarily classes) when I was writing my own vector math to do coordinate transformations. Now that the transformations are baked into the language for both v2 and v3, I don’t need it any longer for that particular reason…but it would still be useful as syntactic sugar to keep related ‘things’ together in one place, like the coordinate positions and color components of objects that are being rendered. I’m working on a firefly simulator and it needs about a dozen separate arrays to handle the coordinates, velocities, lifecycle timing, flash cycle timing, and other attributes. Depending on how you implement address references in the VM, you might get a little speed increase from dereferencing the array pointer once to find an ‘object’ rather than repeating the lookup for each property access.

If you want a full JS implementation for ESPs, moddable is the best I’ve seen, but their execution model is very different (the bytecode compiler resides on the desktop, though they do support downloading ‘libraries’ at runtime) so you’d probably need to do a fair bit of work to have a browser-based pattern compiler. Or you could offer them a boxful of PBs to help with the changes!

1 Like

@wizard, I’m good with any implementation you come up with. The performance advantage and reduction of complexity in pattern code are what I think matters most.

(BTW, there are a couple more functions that are common in shaders that somehow absented themselves from my brain when I was writing about this yesterday. They’re generally useful in any graphics context, and would be good to have around in some form – the blending/interpolation functions, smoothstep(), and mix() )

2 Likes

Easing covers most of this, with smoothstep being a lerp with easing, and mix is a lerp with a weighting

Same as with other bits, I’m in favor of doing it userland code, deciding what is clean and useful, and then adding the best bits into the expression list.

I think things we can’t do in userland, like reading the pixel map, are far more important. Adding what’s missing from JS functionality is another good example.

Nothing stops addng something we have a userland solution/library/example for … Of course. But things we can’t do? I want those more.

1 Like