2d rainbow patterns?

I have all the pieces for this hanging around as part of yet another polar conversion project. Here’s the minimalist version:

(If you haven’t already, you’ll have to set up a 2D map for your display for this to work – go to the Pixelblaze’s Mapper tab, select the “matrix” example, set the width variable appropriately for your display and press the “Save” button near the bottom.)

// Radial rainbow for 2D displays
export var speed = 0.5
export var direction = 1

// UI sliders

// higher is faster
export function sliderSpeed(v) {
  speed = 1-v;  
}

// left = inward, right = outward
export function sliderDirection(v) {
  direction = (v < 0.5) ? 1 : -1;  
}

// pythagorean distance from center of display.  Pixelblaze
// provides normalized x,y coords, so center is always going
// to be (0.5,0.5) regardless of real world display dimensions
function getRadius(x, y) {
  x -= 0.5; y -= 0.5;
  return sqrt(x*x + y*y);
}

// generate a timer - a sawtooth wave that we can
// use to animate color -- the direction flag makes
// it positive or negative, depending on the UI
// slider setting
export function beforeRender(delta) {
  t1 = direction * time(0.08 * speed);
}

// use radius and our timer to color every pixel
export function render2D(index, x, y) {
  hsv(t1+getRadius(x, y), 1, 1);
}
2 Likes

Damn, beat me to it. But very similar to what I was going to do.

Probably the only thing I’d add is a wave size tuner/slider so you could control the length of the rainbow (ie smaller/larger bands of color, going thru the hue spectum faster or slower).

Left an exercise for the new folks wanting to learn to code PB: add that feature. You’ll add a new slider, with a new variable controlled by the slider, and then modify how the hue changes by scaling the radius to your new variable. (Hue is time plus radial length now, so changing radial length adjusts how quickly you run thru the hue of colors.)

@zranger1’s solution always returns values of T adjusted for speed, but then adding radius (which is always between 0 and [quick calc, X and Y are never greater than 0.5, radius is sqrt of X^2 + Y^2, aka sqrt of 0.5 or] .7ish) , is what makes the rainbow effect. So adjusting the speed of change doesn’t currently affect the color band size.

2 Likes

@Scruffynerf, I knew you’d come up with something else cool and fun to do with this!

2 Likes

Bro this is exactly what I was looking for.

playing around with it now!

Thank you!

1 Like

I was trying to figure out how to add this wave slider, but didn’t realize how deep that rabbit hole went. lol

So I was also looking at some of the sound patterns and was looking at adding a sound feature. would I add the code to the bottom of the script that’s already written?

Depends on how sound would interact.

As for how deep the rabbit hole is, adding a new slider (and variable it can control) should be 3-4 lines of code. “Scaling the radius” = multiply the radius by the new variable (which should have a value that varies from 0 (which will mean no rainbow, just the changing color) to say 10, meaning at most it’ll multiply the radius by 10 (which means it’ll be 7ish at most). Hue is a value that loops at 1 (so at 1.01, it’s like .01 again), so it’ll make 7 bands of rainbows. Below 1, it’ll stretch out the rainbow, making wider bands of colors.

So let’s figure out to add sound to this:
The expansion board returns a few sound related variables, including an array of 32 values that condenses the sound it hears. (Think of the classic sound bars, you’d have 32 of them)
It also returns the loudest frequency it heard.

So the easy way: adjust radial length to be influenced by the loudest sound. Now your rainbow circle will pulse depending on the music.

More involved: we didn’t calculate the angle only the radius, we just did a full circle. What if we did? The formula for the angle is atan2 ( y / x ) [corrected from atan]
[Note there was a bug in PB for atan (aka arctan) fixed in most recent firmware, so make sure you’ve updated]
So dividing the circle’s angles into 32 pieces, we could adjust the radial length by the value of the matching bucket… And now it’ll make 32 rainbow arcs that change according to the sound it hears.

I’ll post the code for this in a few days, give the folks who want to learn PB coding some time to try it themselves.

2 Likes

This is a really cool idea @Scruffynerf. What do you think of this? Learning "topic" or competitive learning

1 Like

Bonus points if your rainbow band size scaling solution compensates for something @scruffynerf pointed out: The largest possible distance from center in a rectangular matrix is going to be along a diagonal, and because matrix coordinates are all normalized to the range 0…1, it’s going to have the value sqrt(0.5)==0.7071.

In my code, I use distance as the base for hue, which means that you don’t get the full 0…1 range. Some pinks and reds are potentially missing. I decided to do this for simplicity’s sake. It’s easier to read, and is not visually obvious. But IT’S NOT PERFECT, and it can be. If you’re adding band scaling anyway, you might as well fix it!

(My solution requires changing just two lines, if I’m allowed to put the whole band size slider on a single line. )

1 Like

Actually my quick and dirty solution would be to double the radius, which especially works because despite the square matrix, we’re drawing circles and so the circle is only 0.5 radius, meaning at most we’d do half of the hue spectrum. So just multiply the found radius by 2, which will perfectly fit the spectrum into the circle, and at the corners, be 2*.707 or roughly 1.4, so it’ll be more rainbow but not a double rainbow.

So my fix would be just adding two characters.

[Exact fix removed as I realized it gave away entirely how to do the scaling too]

1 Like

Ha… I figured it was a cleverly concealed very large hint!

1 Like

As promised, circular spectrograph. Rough, so not posting to patterns (yet)… but people can play with it.

// Radial rainbow for 2D displays
// initial circle/rainbow code by @zranger1
// modified for sound by @scruffynerf

export var speed = 0.5
export var direction = 1
export var size = 100
export var spin = 1
export var spincycle = 0
export var spincyclearray = 0

export var frequencyData
export var angle

// UI sliders

// higher is faster
export function sliderSpeed(v) {
  speed = 1-v;  
}

// left = inward, right = outward
export function sliderDirection(v) {
  direction = (v < 0.5) ? 1 : -1;  
}

// sizing
export function sliderSize(v) {
  size = 200*v
}

// spin
export function sliderSpin(v) {
  spin = v
}

// pythagorean distance from center of display.  Pixelblaze
// provides normalized x,y coords, so center is always going
// to be (0.5,0.5) regardless of real world display dimensions
function getRadius(x, y) {
  x -= 0.5; y -= 0.5;
  return sqrt(x*x + y*y)*2;
}

// pythagorean angle from center of display.  Pixelblaze
// provides normalized x,y coords, so center is always going
// to be (0.5,0.5) regardless of real world display dimensions
function getAngle(x, y) {
  x -= 0.5; y -= 0.5;    
  angle = floor(((atan2(y,x)+PI)/4)*32)%32
  angle = (angle+spincyclearray)%32;
  return angle;
}

// generate a timer - a sawtooth wave that we can
// use to animate color -- the direction flag makes
// it positive or negative, depending on the UI slider setting
// spin slider controls the spin, currently only one direction
export function beforeRender(delta) {
  t1 = direction * time(0.08 * speed);
  spincycle = (spincycle + spin)%32
  spincyclearray = floor(spincycle);
}

// use radius+angle and timer to color every pixel, if it's loud enough
export function render2D(index, x, y) {
  radius = getRadius(x, y);
  if (radius <= frequencyData[getAngle(x,y)]*size) {
    hsv(t1+radius, 1, 1);
  } else {
    hsv(0,0,0)
  }
}

there is at least one bug i’m still tracking down. Bonus points for noticing the bug. hint: use a tone generator.

I’ve started a version of this with more solid colors around, rather than rainbow gradients, and with that one, adjusting how many buckets of sound, and which (a sliding range), and it also looks really nice. Coming soon.

3 Likes

This is impressive! I was playing around with the variables, how can I apply this type of sound effects with the pattern that zranger1 created?

I’m confused, this is based on his pattern, I added to it.

If you change the <= in the render to a >, you’ll see the inverse: rainbows everywhere BUT black where the sound is loud enough/right frequency.

I didn’t comment this much. Next version will have more.

The sound activation spins the patterns counter clockwise around the grid, where as Zranger1’s would radiate the color changes from the center —> out or from out ----> in.

Oh it still radiates in and out… That didn’t change.

The only reason it only spins in one direction is that handling the negative spin required extra code I didn’t write (yet). There are 32 buckets and a counter which tracks “spin”. If you subtract instead of add spin, handling the negative cases (when you end up below 0) requires the extra code. Going over is easy to handle (that’s what the %32 (mod 32) does: 33%32 is 1, etc.

The sound doesnt do the spin, only the slider does. The sound is solely size of the spokes.

But as I said, posted for use, it’ll get better.

@scruffynerf, I love the spinning spectrograph! This has been a super cool thing to collaborate on.

Next iteration:
@racker, to keep the rainbow visible, try replacing the render2d() function (at about line 65) with
this:

export function render2D(index, x, y) {
  radius = getRadius(x, y);
  bri = (radius <= frequencyData[getAngle(x,y)]*size) ? 1 : 0.3
  hsv(t1+radius, 1, bri);
}

You can adjust the relative brightness to your liking. The “1” is how bright the spectrograph will be, the 0.3 is how bright the background rainbow will be.

1 Like

This is great!

I replaced the render2d() function with the above and received an error.

“undefined symbol getRadius” this is highlighted in red getRadius(x, y)

It’s there… Check capitalization and spelling, then check to be sure that all the pairs of braces in the functions match up. If you can’t get yours to work, compare it to:

// Radial rainbow for 2D displays
// initial circle/rainbow code by @zranger1
// modified for sound by @scruffynerf

export var speed = 0.5
export var direction = 1
export var size = 100
export var spin = 1
export var spincycle = 0
export var spincyclearray = 0

export var frequencyData
export var angle

// UI sliders

// higher is faster
export function sliderSpeed(v) {
  speed = 1-v;  
}

// left = inward, right = outward
export function sliderDirection(v) {
  direction = (v < 0.5) ? 1 : -1;  
}

// sizing
export function sliderSize(v) {
  size = 200*v
}

// spin
export function sliderSpin(v) {
  spin = v
}

// pythagorean distance from center of display.  Pixelblaze
// provides normalized x,y coords, so center is always going
// to be (0.5,0.5) regardless of real world display dimensions
function getRadius(x, y) {
  x -= 0.5; y -= 0.5;
  return sqrt(x*x + y*y)*2;
}

// pythagorean angle from center of display.  Pixelblaze
// provides normalized x,y coords, so center is always going
// to be (0.5,0.5) regardless of real world display dimensions
function getAngle(x, y) {
  x -= 0.5; y -= 0.5;    
  angle = floor(((atan2(y,x)+PI)/4)*32)%32
  angle = (angle+spincyclearray)%32;
  return angle;
}

// generate a timer - a sawtooth wave that we can
// use to animate color -- the direction flag makes
// it positive or negative, depending on the UI slider setting
// spin slider controls the spin, currently only one direction
export function beforeRender(delta) {
  t1 = direction * time(0.08 * speed);
  spincycle = (spincycle + spin)%32
  spincyclearray = floor(spincycle);
}

// use radius+angle and timer to color every pixel, if it's loud enough
export function render2D(index, x, y) {
  radius = getRadius(x, y);
  bri = (radius <= frequencyData[getAngle(x,y)]*size) ? 1 : 0.3
  hsv(t1+radius, 1, bri);
}
1 Like

Someone built a nice piece using this code:

1 Like

Ha! High five, @scruffynerf! Casually riffing on rainbow code, we made something cool enough that it got used in a nice project. Hard to beat that.

2 Likes