Color palette support?

+1 for lerp(); I use it all over the place for scaling sliders and wave functions.

…and I like the improved preview renderer for WLED. Does Pixelblaze generate a preview once after each edit, or does it send the preview repeatedly over the websocket? Other things like Firestorm and @zranger1’s python library seem to have trouble communicating to PB when the editor is open.

I like that, but any rainbow ones would get missed, etc. But I suspect any autosort will. I just want names to be grouped into human categories, instead of spread spectrum :slight_smile:

I believe the ‘generating’ delay we see is as it re-renders the jpg preview on compile, but the ‘live’ feed? @wizard, is that available other than via the UI? WLED has a /json/live url, which is how Scott is grabbing data in his script

And it does:

//  palettes copied directly from http://dev.thi.ng/gradients/
var paletteGreenRed  = [ [ 0.5, 0.5, 0], [0.5, 0.5, 0], [0.5, 0.5, 0], [0.5, 0, 0] ];
var paletteRainbow1 = [ [0.500, 0.500, 0.500], [0.500, 0.500, 0.500], [1.000, 1.000, 1.000], [0.000, 0.333, 0.667]];
var paletteHarvest =  [ [ 0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [1.0, 1.0, 0.5], [0.8, 0.9, 0.3] ];

//  Calculate and render the palette color at a particular point.
function paletteAt(t, palette) {
  _r = palette[0][0] + palette[1][0] * cos(PI2 * ((palette[2][0] * t) + palette[3][0]));
  _g = palette[0][1] + palette[1][1] * cos(PI2 * ((palette[2][1] * t) + palette[3][1]));
  _b = palette[0][2] + palette[1][2] * cos(PI2 * ((palette[2][2] * t) + palette[3][2]));
  rgb(_r, _g, _b);
}

export function render(index) {
  t = time(0.05);
  x = index/pixelCount;
  if (t < 0.3) paletteAt(x, paletteRainbow1);
  else if (t < 0.6) paletteAt(x, paletteGreenRed);
  else paletteAt(x, paletteHarvest);
}

Nice. Thanks @wizard and thanks @scruffynerf!

@pixie, are you running the editor and Firestorm/python on the same machine? If so, once the editor is up, that’d stop any other program from connecting from the same IP address. The web browser very sensibly doesn’t want to share its open sockets with other apps.

If you need to have the editor and another websocket connection going at the same time, you can set up multiple IP addresses on the same NIC and run Firestorm, etc. on the secondary IP.

I do this all the time. It’s handy for separating traffic for various tasks and services. It also gives you an easy way to get more than one port 80 on a machine, for programs that absolutely insist on that.

On recent Linux w/GUI, just go to the IPv4 settings in the Network manager and add as many addresses as you need.

It’s not much more complicated in Windows, but it’s harder to find to the right place to do it. Here’s a walkthrough of a couple of methods.

To summarize, you’ll need to go to the TCP/IP v4 properties dialog for your network adapter., switch yourself to a static IP address, then click the “Advanced” button and you can add addresses from there. Or you can just use one of the handy command line options in the walkthrough above.

Nice, I’m actually wondering if reversing the way this is put together makes sense…

Using A,B,C, and D do a [R, G, B], so you end up with 4 3-arrayed (RGB) values,
and you calculate a value for each of R, G, B.
But then http://dev.thi.ng/gradients/ offers a global tweak as well, which is kinda nifty.

What if it’s done as R, G, B, and then store A,B,C,D for each? That’s 3 values with an array of 4 in each, but now, the color channels are easily separated… so I could modify the R channel as a whole array, rather than parts of 4 arrays. Not to mention that A,B,C,D are all relative to PI.

Either way, considering seeing if compacting these into a 16.16 also makes sense…
That would make an entire palette reducible down to just a scant 3 or 4 single values. That seems an amazing reduction… and totally worthwhile.

“oh, that palette? It’s 0x12345678, 0x345AE444, 0x13405544, 0xFF00EEDD”

In the end, I didn’t bother with bit-packing the band values, because array initializers make the palette code short and sweet enough on its own:

function LERP(percent, low, high) { return low + percent * (high - low); }
//  A palette is a variable number of bands, each containing a startIndex, R, G, and B component.
function fastLedPaletteAt(v, palette) {
  v = clamp(v << 8, 0, 255); 
  for (idx=0;idx<palette.length;idx++) {
    if (v <= palette[idx][0]) { //  We're at the beginning of the band.
      if (v == 0) { rgb(palette[idx][1] >> 8, palette[idx][2] >> 8, palette[idx][3] >> 8); }
      else {  //  We're in the middle of this band, so LERP to find the appropriate shade.
        scale = (v - palette[idx-1][0]) / (palette[idx][0] - palette[idx-1][0]);
        rgb(LERP(scale, palette[idx-1][1], palette[idx][1]) >> 8, LERP(scale, palette[idx-1][2], palette[idx][2]) >> 8, LERP(scale, palette[idx-1][3], palette[idx][3]) >> 8);
      }
      break;
    }
  }
}

// Gradient palette "quagga_gp" originally from http://soliton.vm.bytemark.co.uk/pub/cpt-city/rc/tn/quagga.png.index.html; converted for FastLED with gammas (2.6, 2.2, 2.5)
var paletteQuagga = [ [ 0,   1,   9,  84], [ 40,  42,  24,  72], [ 84,   6,  58,   2], [ 168,  88, 169,  24], [211,  42,  24,  72], [255,   1,   9,  84] ];
// Gradient palette "scoutie_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/rc/tn/scoutie.png.index.html; converted for FastLED with gammas (2.6, 2.2, 2.5)
var paletteScoutie = [ [ 0, 255,156,  0], [127,   0,195, 18], [216,   1,  0, 39], [255,   1,  0, 39] ];


//  Pattern
export function render(index) {
  t = time(0.05);
  x = index/pixelCount;
  if (t < 0.5) fastLedPaletteAt(x, paletteScoutie);
  else fastLedPaletteAt(x, paletteQuagga);
}
2 Likes

@zranger1, I don’t think that’s the case.

A websockets client such as the one inside a webbrowser can only connect to a single server at a time, but a websockets server such as the one inside the PB’s webserver can handle multiple clients. If a server gets confused by multiple client connections from the same IP address, then it’s not identifying connections appropriately – that is to say, uniquely.

At any rate, PB can currently handle simultaneous connections from the same Windows computer (web UI inside Edge, and your python library connecting from a WSL session), but the python connection frequently disconnects with an error message if the preview band is running in the Editor window; it doesn’t seem to mind if the preview band is running atop the Pattern List page.

Also I forget sometimes that I have an editor window open and open a second, but that makes the response time slow down so drastically that I eventually notice and check the other browser tabs…

This is interesting. You can… I just tested it… open a browser window to a Pixelblaze from multiple different browsers on the same machine simultaneously. I had Edge, Chromium and Firefox going, and everything appears to be working, all simultaneously watching variables change.

I’ll look into this - it’d be interesting to know if the browsers were doing anything different with the socket, or just using a better websocket library. (And actually, being able to run the Python client while you had a browser window open didn’t work at all when I started. Python just refused to connect if the browser already had a websocket connection. This is an improvement!)

I took a closer look half an hour ago and the python library was throwing an exception from inside a call to waitForEmptyQueue(1000) following a call to setActivePattern(). I replaced the waitForEmptyQueue(1000) call with a sleep(1000) and it’s run without error since…

1 Like

I don’t think this should be the case. Older versions had only global state to track settings for whether or not preview data and updates should be sent, but this has improved, and wouldn’t have prevented a WS connection anyhow. Sure each connection does add load, and there is a system limit of 5 (? IIRC) simultaneous connections. That includes any zombie connections waiting to time out. Older versions would also boot every connection if any stalled, but these are all resolved to the best of my knowledge.

@Scruffynerf yes preview data for the first 100 pixels are streamed over the websocket if it’s enabled for that connection. These come in as binary preview frames. WLED seems to do something similar these days, but as a hex string in JSON.

At some point I will lift the 100 pixel restriction and use the pixel map to render 2d/3d live previews. There will be some limit, and I might do something like sending a subset by skipping pixels or something.

1 Like

@Wizard, thanks for keeping up with the small, edge-case network issues! Things have been a lot smoother in the last couple of firmware versions.

And thanks for localizing the Python problem, @pixie. I’ll see if I can find a graceful way to make waitForEmptyQueue() behave itself if the connection momentarily stalls, and will get it checked in ASAP. I’ve got a couple of bug fixes sitting around waiting to go into PyPl anyway.

So something akin to that script could be written to poll the PB, generating a 100pixel graphic. I’m not sure that’s worthwhile given that we already get the preview jpg as linked above (5 seconds at 30fps) But it’s good to know it’s possible. In the case of WLED, it’s more critical as documentation, since the default patterns and palettes aren’t really changable.

So I tried to pack this cosine gradient code down into 16.16 (8 bits for each of 4 arguments, per color channel) , and it turns out the rounding errors of going to 8bit cause enough of a problem (due to cosine and multiplication, small changes quickly multiply effect) that it stops working as expected, so the palette is just wrong.

I’d still like to avoid the arrays in arrays thing though. so I’ll likely just continue to play with a way to optimize this, but attempt #1 to pack tightly was a failure. Weirdly, I’d see exact matches on red, but totally off on green and blue channels, despite that each was doing the exact same math.

If you want to get rid of the nested arrays, just remove the interior square brackets and cross-multiply the array indices:

var paletteRainbow1 = [ 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 1.000, 1.000, 1.000, 0.000, 0.333, 0.667];

//  Calculate and render the palette color at a particular point.
function paletteAt(t, palette) {
  _r = palette[0] + palette[3] * cos(PI2 * ((palette[6] * t) + palette[9]));
  _g = palette[1] + palette[4] * cos(PI2 * ((palette[7] * t) + palette[10]));
  _b = palette[2] + palette[5] * cos(PI2 * ((palette[8] * t) + palette[11]));
  rgb(_r, _g, _b);
}

But unless you’re desperately short of space, why bother trying to byte-pack them? Readability and the ability to cut-and-paste coefficients from the palette designer webpage are features worth having…

2 Likes

Yeah, the single array approach was my next step too. And I really liked the idea of packing it because ideally, you could store a couple of dozen patterns by default and use little space to do so. The 1/4 size was intriguing, but as it turns it, it’s compression is too great and the loss is noticable.

Having played with both FastLED palettes and IQ’s cosine gradients last night:
The FastLED ones are mostly nice for compatibility and porting and being able to say “ok, you want PB to run all of the WLED patterns, we can do that” but fairly limited usage beyond that.
But the IQ ones are hugely useful for PB and work so well, I’m going to likely implement in many uses, because dropping it in as a replacement for H (which right now, is really where most of our pattern color variations come from, just changing the value of hue in some smooth way) seems like it’s loaded in powerful options.

Making a variant of the default time+hue equals rainbow pattern, but having a huge variety of easy to select color options beyond just rainbow seems like a good demo of that. Yes, you could easily write a similar pattern that did smooth variation only in hue between particular values of blue and green for example, but that seems more work than 12 numbers and one quick function.

So I got a bit to review @jeff’s utility code with fresh eyes, now that I’ve played with WLED/FastLED (shortened to WLED, hereafter) palettes and IQ’s palettes. And it’s interesting because Jeff’s approach is more PB centric (0…1, including rollover), but isn’t really compatible with many WLED palettes (which use an explicit palette map, with up to 255 positions, so you can specify a color at position 0, and then say position 128, and it’s a smooth transition between them, but then the next step might be 150, with a smooth transition to there. I think Jeff’s approach is 8 steps, but it’s also HSV compliant, while the WLED and IQ methods are mostly RGB, by default (I believe they could work with HSV, in many cases).

The IQ method of cosine gradients is very much a ‘quick and dirty’ method, but its not really suited to specific custom palettes, but more of a mathematical offering of many palettes. This works from a PB perspective in that it’s using 0…1, it’ll wrap since it’s using cosine math, and it’s relatively light in terms of definition. A mere 12-16 values should be enough. So an IQ palette is absolutely a winner in terms of “I don’t want a rainbow, what else do you have?” type palettes, where you want to replace a color ‘flow’ with something else.

WLED palettes, being focused on 8bit, are essentially tables of position+RGB, with a minimal size of 2 (0[start] and 255[end]) with as many midpoints as you’d like to add (between 1-254). It would be easy enough to modify all of those values to 0…1, so you’d have something akin to Jeff’s palettes, but more flexible, and more ‘standardized’ ie lots of WLED/FastLed users, but also sites like cpt-city use the usual RGB 0-255 ranges but can also use 0…1 for position.
For an example, you compare different formats for sd | Blue-Gloss 0
you’ll notice that the .pg and .c3g versions (among others) are using 0…1 for positioning.

The negative to all of these is that most of them don’t cycle/loop between 0 and 1, so you end up with a ‘hard edge’ if you try to wrap it. This isn’t always an issue, depends greatly on usage. But it does mean it’s not always a clean replacement for just wrapping ala hsv()

As for which (if any) is better, it’ll depend greatly on how (and who) wants to implement palette support in whatever patterns. For example, if you’re porting WLED patterns, you’ll almost certainly want to support palettes as it expects, if you want it to look ‘like the real thing’. But if you are modifying existing patterns that are ok with wrapping, thanks to the use of hsv(), then a wrapping palette method makes more sense.

Moving forward, I think a lot will depend on how cleanly the implementation is, and how ‘featureful’ it is. If we end up with some form of ‘library’ include, then it might not matter, but in the short term, if you need to include all of the “palette code” into your pattern, then a minimal amount of code is going to win the day in most cases. I wouldn’t want a short pattern to require twice as much code for palettes as the new code itself. If Ben decides to include some form of palette support in the firmware at some point, this might change, but… until we demonstrate what’s really useful and would benefit from firmware including, I’m not sure it’s clear what would be worth stashing into firmware.

Including the existing WLED palettes into a port of WLED patterns/effects makes huge sense. I’ll discuss the WLED porting effort elsewhere (there is a thread for it).

If it’s possible to define lots of ‘wrapping’ palettes via IQ’s method, and even make random palettes viable, or have a robust selection in a quick/small bit of code, is that ‘better’ than having some discreet non-linear palettes that require something akin to the WLED/cpt-city style ‘positioning’ method? I don’t know the answer, and there may not be a definitive answer, it probably depends on usage and preference… but we’ll see. My hope is we flesh out “both” methods and see what get used and how. And to that end, I’d like to see @Jeff’s utility code tweaked to support more of the WLED method, along with code above, so we have a solid ‘positional’ palette library. and the IQ method (which is definitely shader friendly) will also get a solid ‘library’ approach, and we can just see what is more useful.

In general we want to map 0…1 to a color value. A composite function (such as IQ’s cosine examples), or a list of colors (palette) are two extremes of how to do it. In XaoS there are a bunch of random
algorithms which use seeded algorithms to populate a palette.

The GIMP has a best-of-both-worlds-ish data structure. It can have multiple segments, each with a left/right endpoint color and an easing function. It wouldn’t be too hard to “compile” a GIMP gradient into a PBscript function.

GIMP gradient editor

1 Like

Hey everyone! Thanks to you all, and some help from @wizard, I have the following working. We converted a bunch of FastLED palettes to 0…1 real numbers, packed the palette arrays together to improve performance, etc. I hope to publish a tool for converting cpt-city palettes at some point.

var secondsPerPalette = 5;

//  Pattern
export function render(index, x, y) {
  t = time(0.04);
  x = t + index / pixelCount;
  paletteIndex = time(secondsPerPalette) * palettes.length;
  fastLedPaletteAt(x, palettes[paletteIndex], 1.0);
}

function LERP(percent, low, high) {
  return low + percent * (high - low);
}

//  A palette is a variable number of bands, each containing a startIndex, R, G, and B component.
function fastLedPaletteAt(v, palette, brightness) {
  var paletteSize, scale, entryOffset, previousEntryOffset
  paletteSize = floor(palette.length/4)
  v = mod(v, 1);
  for (entryOffset=0;entryOffset<palette.length;entryOffset += 4) {
    if (v <= palette[entryOffset]) {
      //  We're at the beginning of the band.
      if (v == 0) {
        //special case zero, no lerp
        rgb(palette[entryOffset+1], palette[entryOffset+2], palette[entryOffset+3]);
      } else {
        //  We're in the middle of this band, so LERP to find the appropriate shade.
        previousEntryOffset = entryOffset - 4;
        scale = (v - palette[previousEntryOffset]) / (palette[entryOffset] - palette[previousEntryOffset]);
        rgb((LERP(scale, palette[previousEntryOffset+1], palette[entryOffset+1])) * brightness,
            (LERP(scale, palette[previousEntryOffset+2], palette[entryOffset+2])) * brightness,
            (LERP(scale, palette[previousEntryOffset+3], palette[entryOffset+3])) * brightness);
      }
      break;
    }
  }
}

// From ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb

// Gradient palette \"ib_jul01_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/xmas/tn/ib_jul01.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var ib_jul01 = [
  0.0, 0.761, 0.004, 0.004,
  0.369, 0.004, 0.114, 0.071,
  0.518, 0.224, 0.514, 0.11,
  1.0, 0.443, 0.004, 0.004,
];

// Gradient palette \"es_vintage_57_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_57.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_vintage_57 = [
  0.0, 0.008, 0.004, 0.004,
  0.208, 0.071, 0.004, 0.0,
  0.408, 0.271, 0.114, 0.004,
  0.6, 0.655, 0.529, 0.039,
  1.0, 0.18, 0.22, 0.016,
];

// Gradient palette \"es_vintage_01_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_01.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_vintage_01 = [
  0.0, 0.016, 0.004, 0.004,
  0.2, 0.063, 0.0, 0.004,
  0.298, 0.38, 0.408, 0.012,
  0.396, 1.0, 0.514, 0.075,
  0.498, 0.263, 0.035, 0.016,
  0.6, 0.063, 0.0, 0.004,
  0.898, 0.016, 0.004, 0.004,
  1.0, 0.016, 0.004, 0.004,
];

// Gradient palette \"es_rivendell_15_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/rivendell/tn/es_rivendell_15.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_rivendell_15 = [
  0.0, 0.004, 0.055, 0.02,
  0.396, 0.063, 0.141, 0.055,
  0.647, 0.22, 0.267, 0.118,
  0.949, 0.588, 0.612, 0.388,
  1.0, 0.588, 0.612, 0.388,
];

// Gradient palette \"rgi_15_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/rgi/tn/rgi_15.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var rgi_15 = [
  0.0, 0.016, 0.004, 0.122,
  0.122, 0.216, 0.004, 0.063,
  0.247, 0.773, 0.012, 0.027,
  0.373, 0.231, 0.008, 0.067,
  0.498, 0.024, 0.008, 0.133,
  0.624, 0.153, 0.024, 0.129,
  0.749, 0.439, 0.051, 0.125,
  0.875, 0.22, 0.035, 0.137,
  1.0, 0.086, 0.024, 0.149,
];

// Gradient palette \"retro2_16_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/retro2/tn/retro2_16.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var retro2_16 = [
  0.0, 0.737, 0.529, 0.004,
  1.0, 0.18, 0.027, 0.004,
];

// Gradient palette \"Analogous_1_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/red/tn/Analogous_1.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Analogous_1 = [
  0.0, 0.012, 0.0, 1.0,
  0.247, 0.09, 0.0, 1.0,
  0.498, 0.263, 0.0, 1.0,
  0.749, 0.557, 0.0, 0.176,
  1.0, 1.0, 0.0, 0.0,
];

// Gradient palette \"es_pinksplash_08_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_08.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_pinksplash_08 = [
  0.0, 0.494, 0.043, 1.0,
  0.498, 0.773, 0.004, 0.086,
  0.686, 0.824, 0.616, 0.675,
  0.867, 0.616, 0.012, 0.439,
  1.0, 0.616, 0.012, 0.439,
];

// Gradient palette \"es_pinksplash_07_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_07.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_pinksplash_07 = [
  0.0, 0.898, 0.004, 0.004,
  0.239, 0.949, 0.016, 0.247,
  0.396, 1.0, 0.047, 1.0,
  0.498, 0.976, 0.318, 0.988,
  0.6, 1.0, 0.043, 0.922,
  0.757, 0.957, 0.02, 0.267,
  1.0, 0.91, 0.004, 0.02,
];

// Gradient palette \"Coral_reef_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/other/tn/Coral_reef.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Coral_reef = [
  0.0, 0.157, 0.78, 0.773,
  0.196, 0.039, 0.596, 0.608,
  0.376, 0.004, 0.435, 0.471,
  0.376, 0.169, 0.498, 0.635,
  0.545, 0.039, 0.286, 0.435,
  1.0, 0.004, 0.133, 0.278,
];

// Gradient palette \"es_ocean_breeze_068_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_068.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_ocean_breeze_068 = [
  0.0, 0.392, 0.612, 0.6,
  0.2, 0.004, 0.388, 0.537,
  0.396, 0.004, 0.267, 0.329,
  0.408, 0.137, 0.557, 0.659,
  0.698, 0.0, 0.247, 0.459,
  1.0, 0.004, 0.039, 0.039,
];

// Gradient palette \"es_ocean_breeze_036_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_036.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_ocean_breeze_036 = [
  0.0, 0.004, 0.024, 0.027,
  0.349, 0.004, 0.388, 0.435,
  0.6, 0.565, 0.82, 1.0,
  1.0, 0.0, 0.286, 0.322,
];

// Gradient palette \"departure_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/mjf/tn/departure.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var departure = [
  0.0, 0.031, 0.012, 0.0,
  0.165, 0.09, 0.027, 0.0,
  0.247, 0.294, 0.149, 0.024,
  0.329, 0.663, 0.388, 0.149,
  0.416, 0.835, 0.663, 0.467,
  0.455, 1.0, 1.0, 1.0,
  0.541, 0.529, 1.0, 0.541,
  0.58, 0.086, 1.0, 0.094,
  0.667, 0.0, 1.0, 0.0,
  0.749, 0.0, 0.533, 0.0,
  0.831, 0.0, 0.216, 0.0,
  1.0, 0.0, 0.216, 0.0,
];

// Gradient palette \"es_landscape_64_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_64.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_landscape_64 = [
  0.0, 0.0, 0.0, 0.0,
  0.145, 0.008, 0.098, 0.004,
  0.298, 0.059, 0.451, 0.02,
  0.498, 0.31, 0.835, 0.004,
  0.502, 0.494, 0.827, 0.184,
  0.51, 0.737, 0.82, 0.969,
  0.6, 0.565, 0.714, 0.804,
  0.8, 0.231, 0.459, 0.98,
  1.0, 0.004, 0.145, 0.753,
];

// Gradient palette \"es_landscape_33_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_33.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_landscape_33 = [
  0.0, 0.004, 0.02, 0.0,
  0.075, 0.125, 0.09, 0.004,
  0.149, 0.631, 0.216, 0.004,
  0.247, 0.898, 0.565, 0.004,
  0.259, 0.153, 0.557, 0.29,
  1.0, 0.004, 0.016, 0.004,
];

// Gradient palette \"rainbowsherbet_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/icecream/tn/rainbowsherbet.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var rainbowsherbet = [
  0.0, 1.0, 0.129, 0.016,
  0.169, 1.0, 0.267, 0.098,
  0.337, 1.0, 0.027, 0.098,
  0.498, 1.0, 0.322, 0.404,
  0.667, 1.0, 1.0, 0.949,
  0.82, 0.165, 1.0, 0.086,
  1.0, 0.341, 1.0, 0.255,
];

// Gradient palette \"gr65_hult_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr65_hult.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var gr65_hult = [
  0.0, 0.969, 0.69, 0.969,
  0.188, 1.0, 0.533, 1.0,
  0.349, 0.863, 0.114, 0.886,
  0.627, 0.027, 0.322, 0.698,
  0.847, 0.004, 0.486, 0.427,
  1.0, 0.004, 0.486, 0.427,
];

// Gradient palette \"gr64_hult_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr64_hult.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var gr64_hult = [
  0.0, 0.004, 0.486, 0.427,
  0.259, 0.004, 0.365, 0.31,
  0.408, 0.204, 0.255, 0.004,
  0.51, 0.451, 0.498, 0.004,
  0.588, 0.204, 0.255, 0.004,
  0.788, 0.004, 0.337, 0.282,
  0.937, 0.0, 0.216, 0.176,
  1.0, 0.0, 0.216, 0.176,
];

// Gradient palette \"GMT_drywet_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/gmt/tn/GMT_drywet.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var GMT_drywet = [
  0.0, 0.184, 0.118, 0.008,
  0.165, 0.835, 0.576, 0.094,
  0.329, 0.404, 0.859, 0.204,
  0.498, 0.012, 0.859, 0.812,
  0.667, 0.004, 0.188, 0.839,
  0.831, 0.004, 0.004, 0.435,
  1.0, 0.004, 0.027, 0.129,
];

// Gradient palette \"ib15_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/general/tn/ib15.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var ib15 = [
  0.0, 0.443, 0.357, 0.576,
  0.282, 0.616, 0.345, 0.306,
  0.349, 0.816, 0.333, 0.129,
  0.42, 1.0, 0.114, 0.043,
  0.553, 0.537, 0.122, 0.153,
  1.0, 0.231, 0.129, 0.349,
];

// Gradient palette \"Fuschia_7_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/fuschia/tn/Fuschia-7.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Fuschia_7 = [
  0.0, 0.169, 0.012, 0.6,
  0.247, 0.392, 0.016, 0.404,
  0.498, 0.737, 0.02, 0.259,
  0.749, 0.631, 0.043, 0.451,
  1.0, 0.529, 0.078, 0.714,
];

// Gradient palette \"es_emerald_dragon_08_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/emerald_dragon/tn/es_emerald_dragon_08.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_emerald_dragon_08 = [
  0.0, 0.38, 1.0, 0.004,
  0.396, 0.184, 0.522, 0.004,
  0.698, 0.051, 0.169, 0.004,
  1.0, 0.008, 0.039, 0.004,
];

// Gradient palette \"lava_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/lava.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var lava = [
  0.0, 0.0, 0.0, 0.0,
  0.18, 0.071, 0.0, 0.0,
  0.376, 0.443, 0.0, 0.0,
  0.424, 0.557, 0.012, 0.004,
  0.467, 0.686, 0.067, 0.004,
  0.573, 0.835, 0.173, 0.008,
  0.682, 1.0, 0.322, 0.016,
  0.737, 1.0, 0.451, 0.016,
  0.792, 1.0, 0.612, 0.016,
  0.855, 1.0, 0.796, 0.016,
  0.918, 1.0, 1.0, 0.016,
  0.957, 1.0, 1.0, 0.278,
  1.0, 1.0, 1.0, 1.0,
];

// Gradient palette \"fire_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/fire.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var fire = [
  0.0, 0.004, 0.004, 0.0,
  0.298, 0.125, 0.02, 0.0,
  0.573, 0.753, 0.094, 0.0,
  0.773, 0.863, 0.412, 0.02,
  0.941, 0.988, 1.0, 0.122,
  0.98, 0.988, 1.0, 0.435,
  1.0, 1.0, 1.0, 1.0,
];

// Gradient palette \"Colorfull_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Colorfull.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Colorfull = [
  0.0, 0.039, 0.333, 0.02,
  0.098, 0.114, 0.427, 0.071,
  0.235, 0.231, 0.541, 0.165,
  0.365, 0.325, 0.388, 0.204,
  0.416, 0.431, 0.259, 0.251,
  0.427, 0.482, 0.192, 0.255,
  0.443, 0.545, 0.137, 0.259,
  0.455, 0.753, 0.459, 0.384,
  0.486, 1.0, 1.0, 0.537,
  0.659, 0.392, 0.706, 0.608,
  1.0, 0.086, 0.475, 0.682,
];

// Gradient palette \"Magenta_Evening_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Magenta_Evening.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Magenta_Evening = [
  0.0, 0.278, 0.106, 0.153,
  0.122, 0.51, 0.043, 0.2,
  0.247, 0.835, 0.008, 0.251,
  0.275, 0.91, 0.004, 0.259,
  0.298, 0.988, 0.004, 0.271,
  0.424, 0.482, 0.008, 0.2,
  1.0, 0.18, 0.035, 0.137,
];

// Gradient palette \"Pink_Purple_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Pink_Purple.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Pink_Purple = [
  0.0, 0.075, 0.008, 0.153,
  0.098, 0.102, 0.016, 0.176,
  0.2, 0.129, 0.024, 0.204,
  0.298, 0.267, 0.243, 0.49,
  0.4, 0.463, 0.733, 0.941,
  0.427, 0.639, 0.843, 0.969,
  0.447, 0.851, 0.957, 1.0,
  0.478, 0.624, 0.584, 0.867,
  0.584, 0.443, 0.306, 0.737,
  0.718, 0.502, 0.224, 0.608,
  1.0, 0.573, 0.157, 0.482,
];

// Gradient palette \"Sunset_Real_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Sunset_Real.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Sunset_Real = [
  0.0, 0.471, 0.0, 0.0,
  0.086, 0.702, 0.086, 0.0,
  0.2, 1.0, 0.408, 0.0,
  0.333, 0.655, 0.086, 0.071,
  0.529, 0.392, 0.0, 0.404,
  0.776, 0.063, 0.0, 0.51,
  1.0, 0.0, 0.0, 0.627,
];

// Gradient palette \"es_autumn_19_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/autumn/tn/es_autumn_19.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var es_autumn_19 = [
  0.0, 0.102, 0.004, 0.004,
  0.2, 0.263, 0.016, 0.004,
  0.329, 0.463, 0.055, 0.004,
  0.408, 0.537, 0.596, 0.204,
  0.439, 0.443, 0.255, 0.004,
  0.478, 0.522, 0.584, 0.231,
  0.486, 0.537, 0.596, 0.204,
  0.529, 0.443, 0.255, 0.004,
  0.557, 0.545, 0.604, 0.18,
  0.639, 0.443, 0.051, 0.004,
  0.8, 0.216, 0.012, 0.004,
  0.976, 0.067, 0.004, 0.004,
  1.0, 0.067, 0.004, 0.004,
];

// Gradient palette \"BlacK_Blue_Magenta_White_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue_Magenta_White.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var BlacK_Blue_Magenta_White = [
  0.0, 0.0, 0.0, 0.0,
  0.165, 0.0, 0.0, 0.176,
  0.329, 0.0, 0.0, 1.0,
  0.498, 0.165, 0.0, 1.0,
  0.667, 1.0, 0.0, 1.0,
  0.831, 1.0, 0.216, 1.0,
  1.0, 1.0, 1.0, 1.0,
];

// Gradient palette \"BlacK_Magenta_Red_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Magenta_Red.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var BlacK_Magenta_Red = [
  0.0, 0.0, 0.0, 0.0,
  0.247, 0.165, 0.0, 0.176,
  0.498, 1.0, 0.0, 1.0,
  0.749, 1.0, 0.0, 0.176,
  1.0, 1.0, 0.0, 0.0,
];

// Gradient palette \"BlacK_Red_Magenta_Yellow_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Red_Magenta_Yellow.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var BlacK_Red_Magenta_Yellow = [
  0.0, 0.0, 0.0, 0.0,
  0.165, 0.165, 0.0, 0.0,
  0.329, 1.0, 0.0, 0.0,
  0.498, 1.0, 0.0, 0.176,
  0.667, 1.0, 0.0, 1.0,
  0.831, 1.0, 0.216, 0.176,
  1.0, 1.0, 1.0, 0.0,
];

// Gradient palette \"Blue_Cyan_Yellow_gp\", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Blue_Cyan_Yellow.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
var Blue_Cyan_Yellow = [
  0.0, 0.0, 0.0, 1.0,
  0.247, 0.0, 0.216, 1.0,
  0.498, 0.0, 1.0, 1.0,
  0.749, 0.165, 1.0, 0.176,
  1.0, 1.0, 1.0, 0.0,
];

// Single array of defined cpt-city color palettes.
// This will let us programmatically choose one based on
// a number, rather than having to activate each explicitly
// by name every time.
//
// This list of color palettes acts as a \"playlist\"; you can
// add or delete, or re-arrange as you wish.
// Count of how many cpt-city gradients are defined:
var palettes = [
  ib_jul01,
  es_vintage_57,
  es_vintage_01,
  es_rivendell_15,
  rgi_15,
  retro2_16,
  Analogous_1,
  es_pinksplash_08,
  es_pinksplash_07,
  Coral_reef,
  es_ocean_breeze_068,
  es_ocean_breeze_036,
  departure,
  es_landscape_64,
  es_landscape_33,
  rainbowsherbet,
  gr65_hult,
  gr64_hult,
  GMT_drywet,
  ib15,
  Fuschia_7,
  es_emerald_dragon_08,
  lava,
  fire,
  Colorfull,
  Magenta_Evening,
  Pink_Purple,
  Sunset_Real,
  es_autumn_19,
  BlacK_Blue_Magenta_White,
  BlacK_Magenta_Red,
  BlacK_Red_Magenta_Yellow,
  Blue_Cyan_Yellow,
];

5 Likes

FYI to anyone finding this post via search, since it’s still a top result for “PixelBlaze Palettes”: This is now natively solved in PixelBlaze.

You can check out a post about how to use the new Palette functions here: How to modify existing patterns to use Palette functions in v3.30 - #10 by ZacharyRD and you can read the release notes about it here: V3.30 Release - palettes, Perlin noise, and much more! – if you’ve always wanted FastLED style palettes in PixelBlaze, you now have them! Enjoy.

2 Likes