"Fairy" lights in a matrix on a clear vinyl sheet

Just like the title says - I started to gluing these Sparkfun addressable fairy lights to a clear vinyl sheet with the intention on running two 16x25 matrixes to hang in my front windows on holidays etc. I settled on 4cm spacing as opposed to the 5cm the lights come with for two reasons, one I think 4cm will “work” better in a matrix inherently, and two the wire doesn’t easily allow 5cm spacing I’d have to bend and stretch the brittle wire to get it there.

Hopefully the result looks okay. I really wanted something that I could leave up for days and not have the light blocked from my window - and more importantly something easy to craft :sweat_smile:


Looking good! I just love the glow with the hot glue! That you for sharing the build progress, its neat to see things come together!

Turns out finding time to glue LEDs when you have two kids under 3 is pretty tough! :sweat_smile:

But I managed to do some tonight, hoping to be inspired and maybe sacrifice a few hours of sleep to get it finished!


Ooh that’s turning out nice!

1 Like

I did it (well, one panel of two I was planning). I have a question @wizard is it possible to “mask” the pixel map, for example say to make a star shape or a circle shape and have the patterns all display within this? I have made my map as the panel is physically represented but I’m wondering how I can take certain pixels and have them turned off completely so that no pattern can use them.

1 Like

Yes, the secret is to assign those pixels values where they won’t turn on. You have a few options:

  1. pick a extreme value, instead of the real value, so that you can identify which pixels to keep off. Since mapping renormalizes all values, you’ll want to assign some values that you can identify without breaking the rest of your map. So if your map values run from 0 to 30 (for example), assigning a bunch to 100 will skew all of your map badly on renormalizing, but assigning 31 to them all will set that as the extreme edge (close to 1 in the renormalized map.). Then for each pixel, in render(), if the value is “too high” (close to 1), do nothing (or send all zeros), and they won’t light up.

  2. do it outside the map. While this requires more code per pattern, it’s super flexible, and let’s you do multiple things like one pattern will be a circle and another will be a star, etc.

Just make a list of what pixel index numbers to keep off… Set those into an array at the start of your pattern, and then, in render(), if the index value is in that array, instead of whatever your pattern is supposed to do, just do nothing (send zeros).

If you really want to limit ALL patterns (so even unmodified patterns only do a star shape, for example), there is no good way to do it. At some point, you’ll need to modify code I think. Given that, option 2 is way way more flexible (since you can adjust the “mask”, and even make multiple masks if you wish) so I’d do it that way, rather than in the map.

Maybe @wizard or @jeff or @zranger1 has a better idea, but I can’t think of one.

Actually by mentioning @zranger1 , I did think of a 3rd option, which is to use the shader math approach, and calculate the SDF (signed distance field) “mask” on top of the pattern, and if outside the desired shape, do nothing. That’s actually how we do a lot of fun stuff anyway.
Sorry I didn’t flesh this out more but it’s described elsewhere on this forum, also see GitHub - zranger1/SDF-LED: Tools for using signed distance fields and other shader-influenced rendering techniques with 2D/3D addressable LED controllers.

First of all, that is a nicely shot video. It’s hard to capture LED colors, and you really got it! Whats your secret?

I wouldn’t recommend doing this on the pixel map for a number of reasons.

Yes, I was going to say the shapes morphing pattern would be a good place to start! Check it out:

Geometry Morphing Demo 2D.epe (17.9 KB)
(Also on the pattern site, page 2)

I was curious how easy/performant this was, so I mixed the star shape with spiral twirls. It came out quite gorgeously, and ends up at 88% FPS after swapping in hypot, atan2, and coordinate transforms.

spiral twirls star 2D.epe (13.1 KB)

The key code that I had to add were these bits:

export function render2D(index, x, y) {
  if (hexStar(x, y, objectSize) > lineWidth)
  //... the rest of the pattern code

//from Geometry Morphing Demo
function signum(a) {
  return (a > 0) - (a < 0)

function hexStar(x,y,r) {
  // rescale to pointy parts of star
  x = abs(x*1.73205); y = abs(y*1.73205); 
  dot = 2 * min(-0.5*x + 0.866025 * y,0);
  x -= dot * -0.5; y -= dot * 0.866025;
  dot = 2 * min(0.866025*x + -0.5 * y,0);
  x -= dot * 0.866025; y -= dot * -0.5;
  x -= clamp(x, r * 0.57735, r * 1.73205);
  y -= r;
  return signum(y) * hypot(x,y) / 1.73205;

This also relies on using the coordinate transform API to center and scale things, but if you don’t want to update an existing pattern for that that you could adjust x,y as you pass them in to the hexStar function. Set objectSize and lineWidth as needed.

1 Like

Very low brightness, I had it at 10 or 20 I think.

Darn it would be really nice to define something as a global. Like in mapper a little area where you can define a render2d as well which will run before the pattern’s render2d. Or just to be handled in the map itself with a null or something… oh well thanks for the workarounds! I can certainly code it that’s not my problem it’s with my free time for this sort of thing :laughing:

I’ll just add one other pragmatic <bad?> option which optimizes for ease of changing the mask in one place, and minimal code changes in every pattern to apply a new mask.

It’s similar to @Scruffynerf’s option 1, but it would be to assign the mask 1|0 in the Z coordinate. This way, if you wanted to switch the mask shape for all patterns on a board, you’d only have to swap out the map generator and not edit all the patterns individually. It also allows you to use most existing 2D code from the library without transforms. The library patterns usually don’t assume an extreme x,y value in the map (so you don’t need to correct them for the resulting skew / scale).

For example, any pattern with a render2D(index, x, y) can have the following pasted in:

export function render3d(index, x, y, maskAllows) {
  (maskAllows > .5) ? render2D(index, x, y) : hsv(0, 0, 0)

Any 1D pattern can use a similar approcah:

export function render3d(index, x, y, maskAllows) {
  (maskAllows > .5)  ? render(index) : hsv(0, 0, 0)

And to use a 3D pattern, just rename/wrap the existing render3D(), or add this at the beginning:

export function render3d(index, x, y, maskAllows) {
  if (maskAllows < .5) { hsv(0, 0, 0); return }   //  Paste this line at the top of render3d)
  // <existing 3D renderer code>

Nice use of the Z coordinate as a ‘on/off’… I really really like that, WAY better than my answer. This is why I tagged you, @jeff . This is my new fav answer. It wouldn’t work with a true Z coordinate in use, and I’m not 100% sure the 0/1 wouldn’t be renormalized so you’d likely need to round it to get either 0 or 1 values back (having them set to something like Z = 0.001 and .998 would be more likely I think)

But absolutely the way to go. The SDF way is super awesome and @wizard’s example is a good one… but the reason it was #3 was that it’s not mapped at all, and not ‘whatever you want to do’ either. You need to define the shape via math, rather than just list pixel ids (which was #2). With the above idea of using Z as on/off, you could pretty easily have map code that turned ‘off’ whatever pixels you wanted, as specific as you like. And the added ‘pattern code’ is pretty simple.

1 Like

Ah! Great point about Z normalization. I edited the answer above to turn them all into (maskAllows > .5) conditionals!

This is more like it, great workaround!

Mine turned 6 this year and is in school now, and all of a sudden I have all the time in the world again. So… Be sure to save projects for when they’re 6 :).

Losing project time is something I really struggled with for those 6 years. But getting it back was sudden… and I miss hanging out w/ my kid all day.

Sorry for the OT. Your project looks great!


No I appreciate it, I never could have imagined the extent kids require, the experience is vastly more challenging than my predictions haha

Good to hear you’ve got project time back, I’m so desperate for some it’s become as elusive as winning the lottery!

1 Like

It also doesn’t have to be binary: if it’s 0…1 then you could multiply by the Z value. For example, gradient mask, antialiased shapes, or any image revealed by persistence of vision.

Or, say, something more complicated than simple multiplication …

if (z <= 0.5) {
    Value = Value*z
} else {
    Value = (1+sqrt(Value*z))/2 
    Saturation = Saturation - (2*z) - 1
1 Like

I stuck the completed section up on the window, looks great it’s very stealth and plenty bright, 40% is a reasonable match to my Christmas lights. I wish I had time to do the second section! Also does anyone know if there are any patterns with christmas shapes already made? Like a tree with lights or a candy canes or something?


Really clever build and stunning results. As wizard said the illumination of the hot glue is fantastic. Diffusion is such a visual multiplier. I also love the transparent background. It looks great in the window and doesn’t block the light.

Hope you find the time you need to complete the second one!


Thanks! I was surprised upon looking for low power, small addressable LED products there really weren’t many out there. Ideally i was hoping to buy a mesh like “curtain” pre fabricated but never did find one made of these!

1 Like

I’ve seen them, but they are quite expensive (hundreds of dollars). Your method is way better.

What sort of vinyl did you use? Thickness? Cost?

We don’t have a lot of matrix-y “display” patterns (yet?), Especially ones that work on arbitrary sizes.
Doable, but not yet. Likely some SDF shapes.can be done ( @zranger1 and I were collecting shapes but we’ve both been focused elsewhere)

@jeff 's polar Christmas topper might have some good stuff for you. But it’s polar mapped, and I wonder if it needs to be to rewritten for normal matrixes? @jeff ?

1 Like

I found a vendor on aliexpress selling fairy wings full of LEDs … but not programmable, and when I asked they said they couldn’t do it. :confused: