Significant feature release: Sync multiple Pixelblazes

Hi everyone,

We have an exciting release that will allow you to:

  1. Group Pixelblazes and control them all from one device’s web UI (launch patterns, run playlists, set global brightness)
  2. Send sensor board data (audio, for example) to all group members :speaker: :partying_face:
  3. Live code patterns and have them update on all group members in real-time

The synchronized pattern control is a little like using Firestorm, but you don’t need to have the same patterns on all the devices ahead of time (and obviously you don’t need a separate computer or Raspberry Pi).

This works when one Pixelblaze is in AP mode too, creating a WiFi network, so you don’t need any internet connection or network gear at all.

If you try it out, we’d love your feedback. To keep the forum free from things likely to change quickly, please give us feedback via email (support@electromage.com) or in a DM.

This release also includes:

  1. WS2814 color order support!
  2. A much-improved timesync algorithm. A group will self-synchronize, and the synchronization accuracy has been improved.
  3. Transitions (fade-through-black) between patterns when using Shuffle or Playlists.
  4. Background preloading of the next pattern means switches are smooth and fast, with or without transitions.
  5. Doubled playlist size to 64 entries.
  6. Nicer status information and a drop-down with more detail, including local Pixelblazes on the network, making it easier to find things.

How to use the new sync features

You’ll designate one of your Pixelblazes as the group leader. This is the device you’ll use the UI on most, and the one that can broadcast its sensor board data to others.

In this example, we have three Pixelblazes, each controlling an 8x8 matrix (the Beautiful Box).

 

On the leader's Settings page, you can see a list of peer devices that you can add as a follower.

 

You can also select from available leaders on a follower's Settings page. Here we can see that Box B and Box C are set to follow Box A:

Follower settings for leader, annotated

 

Sometimes you might want a pattern’s code to do different things on a particular follower. To do this, you can give assign each Pixelblaze a distinct Node ID that can be read in pattern code:

Monosnap Pixelblaze Box B 2023-04-18 15-30-44

 

As for mapping, there are two approaches we've been playing with. The simpler of the two is to add "phantom pixels" that will compress the respective maps into where you'd like them relative to each other. For example:

 

To illustrate the use of Node IDs, let's make a single pattern on the leader (Box A) that will play simultaneously on all three, but will change the hue based on which node it's executing on.

Consider this pattern:

near = (a, b, width) => clamp(1-mod(abs(a-b), 1) / width, 0, 1)

export function render2D(index, x, y) {
  // Waves from center
  v = near(hypot(x-.5, y-.5), time(.05), .15)
  
  hsv(0, 0, v*v*v)
}

 

We can change the code to use a different color based on the `nodeId()`:
var hue, red = 0, purple = .8, blue = .6

near = (a, b, width) => clamp(1-mod(abs(a-b), 1) / width, 0, 1)

export function render2D(index, x, y) {
  // Waves from center
  v = near(hypot(x-.5, y-.5), time(.05), .15)
  
  if (nodeId() == 0) {        // Box A - left
    hue = red
  } else if (nodeId() == 1) { // Box B - center
    hue = purple
  } else if (nodeId() == 2) { // Box C - right
    hue = blue
  }
  
  hsv(hue, 1, v*v*v)
}

Installing the Update

If your Pixelblaze is online, you can update right from the Settings tab!

Otherwise, here are instructions for manually installing the update. First, download the appropriate Signed Transfer Firmware Update file:

I recommend that you upload these using the /recovery.html tool, since you can get progress information. You can use the firmware update in Settings at any time to revert to the current stable version.

Let us know how it goes!

12 Likes

This is amazing! I’ll see if I can set up a test bed for this.

One thing that I wonder if it would be easy to add, is to just share the sensor data, but not sync the patterns.

The use case I’m thinking of is that I have one audio source, and 2 or more sets of lights, and I want to run different patterns on them, but they are doing it based on the same audio frequency inputs.

1 Like

To keep things sane, and avoid all the headache of pattern file management across nodes, right now everything in a group runs the same pattern code.

You can fork within the pattern based on the node ID, so it wouldn’t be too hard to make specific PBs do specific things like respond to different frequencies or call different render functions. There’s a trivial example of this above, where each box renders a different color, but of course isn’t limited to that.

1 Like

This is awesome, and exactly what I need for a project. Looking forward to trying this out soon, since my bulk buy of LEDs just arrived from China…

1 Like

Squeezing more code into the music sequencer will be an interesting bit based on how that one is written. I’ll have to mess around with it, but I imagine I just do two separate sequences in the one pattern at the bottom of the file based on the node, but not sure how I’d do the mapping quite right on that. Will have to noodle on it a bit.

1 Like

w00t! I’m gonna launch this and give it a solid go! Thanks for this awesome work!

1 Like

@wizard - this is great! i got two questions …

if you remember last summer, i built a “backfire” sign that had about 2000+ leds mapped over 7 letters. i posted it up in this forum (will find the thread if you need a reference)

the sheer number of leds meant the PB was running most patterns at ~10-15fps.

would splitting up the letters and using the sync also speed up the framerate? i’d be happy to invest in multiple PBs, if it meant a real performance improvement. how would the sync work if the max framerates on each PB were different (due to different number of pixels)… would it just lock to the slowest framerate or the framerate of the master?

second question - i see you’re dealing with mapping by using phantom offsets. ive been considering an enchancement to the backfire sign, by adding a “border” of led strip on the sign frame. ideally id like that to also particpate in the mapping, but this means the border would be enclosing the letters, and not offset in any particular direction.

looking forward to trying this out!

1 Like

Yes, absolutely. That is one of the key advantages of this system, you can split up your LEDs and add more compute power.

Anything that uses time() will work perfectly, without any additional work. It works just fine with different frame rates. More fps just updates faster and smoother, but if they are high enough or close enough to each other then there won’t be any perceptible difference.

Some patterns fade by a percent each frame, and with a little more math can fade consistently across different frame rates by using the delta in milliseconds between frames given in beforeRender. Like calculating compound interest, but for fading pixels.

More on the mapping stuff in a bit, but the short answer is that it’s possible.

Adding on to Wizzard’s answer:

Yes, absolutely. This is one of the key benefits. It also allows you to wire up projects flexibly, because you don’t have to worry as much about signals degrading over longer runs of wire to first pixel or between segments.

Each Pixelblaze will continue to calculate its segment at the maximum possible framerate. In our testing, this looks pretty good across patterns. While there might be something noticeable between a stressed 10FPS segment and some other 30 FPS, it’s usually better than seeing the entire thing run at 5 FPS.

The one thing we’ve found to look out for are patterns that rely on delta (time elapsed between frames) to control decays. For example, some patterns dim elements between frames in a way that depends on FPS. If you see any of these:

brightness = brightness * 0.95
or even the sightly-better:
brightness = brightness * 0.95 / delta

These will decay at different rates for different FPS. There’s a ratio-of-logarithms way to do this that is FPS-independent. We’ll probably want to update all example patterns to do it the right way.

This can still be done with the phantom pixel approach. You might chose to have the border’s Pixelblaze have no phantom pixels, so it goes from (0,0) → (0,1) → (1,1) → (1,0). Then you’d add the phantom pixels to the pixel map on each letter or sub-segment to shrink them down in the overall world coordinates. Maybe your first “B” in “Backfire” would add phantom pixels that scaled it down to fit inside a bounding rectangle (.1,.4) to (.15, .6).

An alternative approach is to use the spacial transform functions (translate(), scale()) within each pattern’s code. You might use the mapper so that the “B” is full scale, filling (0,0) -->(1,1), but then your pattern code says, “Hey if this is nodeId()==1, this is letter B, so scale it down and shift it to the left in the overall 0…1 world coordinates”. This is definitely the way to go if any of your LEDs are moving relative to each other, like two dancers wearing costumes with accelerometers or something. The downside to this approach is that the transforms need to be ported to all patterns. We’ve discussed ways to make this simpler in the future, in terms of having both a local pixel map and a global arrangement. I think of how we arrange multiple monitors as an analogy.

2 Likes

@wizard @jeff - thanks the detailed answers! i have an extra PB on hand right now so I’ll split the sign in half and go from there.

can i also just add that this would be an absolutely ideal, fantastic time to introduce a low-cost “follower” PB… i admit i’m ignorant to the actual costs of the components, assembly etc, but with the pico only being $4 cheaper than the full size PB, and at $35 each, it definitely becomes a cost factor when thinking about possibilities for art installations that use the new sync feature.

Working like a charm so far and I am DEFINITELY noticing a speed boost. Not to mention not having to bring the laptop and router with me everywhere I go. Also because of the updates to how the Sensor boards integrate, I’m gonna look in to attaching the one that’s been collecting dust on my desk. It’s always seemed to complicated for my skill level to integrate but this puts it within reach. I’d LOVE to see the cube moving to some music. Thanks so much for this feature!

Also this solves the occasional Firestorm headache when I first boot up the cube. Sometimes I’d have to spend up to 10 minutes constantly rebooting and restarting the FS server and the PBs until they finally saw each other and played nice.

So while I’m here somewhat off-topic, ever since I started using Firestorm (and the problem has carried over to the new Sync) None of the PBs are saving any of the mapping information. It’s not a huge deal and only takes a minute to re-enter but thought it was worth mentioning.

1 Like

OK, I know this is silly but - on most of my browsers, the green “Save” button beneath the mapping code area isn’t visible by default – you have to scroll. Can I just verify that you’re indeed pushing that button?

1 Like

@Jeff and @Wizard, phantom pixels are ok for now, but in my ideal world, there’d be a full transform matrix - translate, scale, rotation - associated with each Pixelblaze’s map and applied (and the resulting map saved) after normalization.

So the world coordinate space would still be 0.0 to 1.0 in all three dimensions, and each Pixelblaze’s map would occupy just the portion that it needed. UI for this might let the user move the local map around world space with sliders or by entering numbers, then generate and save the matrix.

3 Likes

oh hey - i wanted to ask before i jumped into flashing the beta firmware.

right now the backfire sign has one PB with the pro expander hardware. i was going to take 4 of the 8 letters off the pro expander and run it on the 2nd PB, those four letters hooked up serially since I don’t have another expander.

does the sync feature also work w/ the port expansion feature? just wanted to make sure i didn’t travel down any dead-ends due to the beta software

I dont wanna answer this question :laughing:

2 Likes

Yes, absolutely works with an output expander on one or multiple Pixelblazes.

That’s exactly what I have in mind! Right now resetTransform() just reverts to an identity matrix but the idea is that this would be some transform from the map tab. Could be used to quickly rotate, flip, or scale within a larger world.

Had plans to handle the aspect ratio this way too.

There’s a math benefit to doing it this way too, the map would still be stored normalized using the full resolution of the pixel map (16 bits each element) and all transforms happen in floats so you would get higher quality output.

No idea what the UI would look like for that yet. The order of operations are important for matrix transforms, so a few sliders might not cover it fully especially when rotation comes into play. Might work for most things though. Positioning a multi PB system with that would be tricky too,
You’d want to see where the others are in relation to the mapped one.

In the meantime, I’ve been doing that in code.

I’m playing around with an idea using the image pixel mapper tool to spatially locate and scale multiple PBs. E.g to get a bounding box for each and apply a scale and translate transform based on node id. Doesn’t handle rotation, but that’s been easy enough with a lookup table, at least for simple cases.

2 Likes

I finally got everything working (had to resetup because I forgot to password protect it) and I just wanna say it is SO nice to be able to control this thing from my phone. I didn’t realize how much setup I was doing before up until being able to turn on my phones Wifi and just connect (And remembering to save the mapping lol. Christ) Definitely my favorite new feature and update by far. Thank you so much for this.

Bringing the cube to a fundraiser in Austin this weekend but once I get back I’m diving in to the sensor board compatibility.

2 Likes