Multiple static, pixel specific patterns (Piano Light Guide)

I am looking to create a piano light guide that can help indicate different keys:


But also to be able to flip to show various pre-programmed control mechanisms

Here’s some examples of what I have got and what I am after:

From bottom up:

  • Actual Pixels: These are the pixel numbers as set up on the piano at the moment
  • Possible If Easier: With some PITA I could reverse the pixels if it made it easier to program.
  • C Major: Another example of a scale I would like to show, with a standard repeating pattern.
  • G Blues: An example of a scale I would like to show, with a standard repeating pattern
  • Specific Sections eg: An example a split specific section.
  • Specific Sections option 2: I know this is an option with existing code examples, but I’d prefer the above.

This is the best I’ve gotten so far, and it’s obviously far from ideal:

export function render(index) {

if (index == 143) {
hsv(1, 0, 1)

else if (index == 141) {
hsv(1, 0, 1)
else if (index == 139) {
hsv(1, 0, 1)
else if (index == 137) {
hsv(1, 0, 1)
else if (index == 135) {
hsv(1, 0, 1)
else if (index == 133) {
hsv(1, 0, 1)

else { // index > 200
hsv(0, 0, 0)

I’d be happy with the above code if I could set it once for an octave, but then have it repeat…

That would be something like:

key = index % 11; // if the first LED is on low C, then at every C, key = 0

if (key == 0) {
  // colour LED for C
} else if (key == 1) {
  // colour LED for C#
... etc ...

You could also set up an array of colours in beforeRender() and then just look up colors[index % 11] for each pixel.

Oooh. I think this reply is very good timing. Feel like I’ve just made a breakthrough with the below, with the F Sharps working, but I knew it wasn’t quite right - suspect you’ve given me the missing piece.

var thing = 143 - 22;
var baseFSharp = 143;
var oneFSharp = baseFSharp - 24;
var twoFSharp = baseFSharp - 48;
var baseG = 141;
var baseAFlat = 139;
var Basea = 137;
var baseBFlat = 135;
var baseB = 133;
var baseC = 131;
var baseCSharp = 129;
var baseD = 127;
var baseEFlat = 125;
var baseE = 123;
var baseF = 121;

export function render(index) {

//F Sharp
if (index == baseFSharp || index == oneFSharp || index == twoFSharp) {
hsv(1, 0, 1)

else { // index > 200
hsv(0, 0, 0)

Using your example, it is missing the first instance of the key - any way around that?

…actually forget about that. I need to make the strip go left to right same as the keyboard. It will hurt my head less.

Bugger! Between the slight mismatches between the strip and the keybed, as well as the intra-strip soldering, it’s not predictably indexable all the way up.

The below is working, but could it be done smarter?

var ZeroBFlat = 0;
var BFlatOne = ZeroBFlat + 24;
var BFlatTwo = ZeroBFlat + 48;
var BFlatThree = ZeroBFlat + 72;
var BFlatFour = ZeroBFlat + 95;
var BFlatFive = ZeroBFlat + 119;
var BFlatSix = ZeroBFlat + 143;

export function render(index) {

//B Flat
if (index == ZeroBFlat || index == BFlatOne || index == BFlatTwo || index == BFlatThree || index == BFlatFour || index == BFlatFive || index == BFlatSix) {
hsv(1, 0, 1)

else { // index > 200
hsv(0, 0, 0)

As my monologue continues, I am now searching for options on 74 led/m strips…which somehow exist, surprisingly!

(BTW you can write ``` javascript (…lines of code…) ``` with each ``` at the beginning of a line to make a code block instead of a simple blockquote. Putting “javascript” on the opening line tells the forum how to colour the code!)

When the index is 95 or above, you really wish it was +1 so your code could be regular. So just adjust the index on the fly …

if (index >= 95) { index += 1 }

if (index % 24 == 0) {
    // it's a BFlat of some sort
} else {
    // it's not

The basic idea is to adjust the “input” which in this case is the index number, so that you can retain whatever is left of your sanity while writing the rest of your code. :relaxed:

1 Like

Come to think of it, looking at your diagram, you could also make an array of the numbers that you want, with -1 as the value where you’re not going to use a pixel at all:

var fakeindex = [0,1,-1,2,-1,3,4,5,-1,6]

then in render():

index = fakeindex[index]
if (index == -1) { rgb(0,0,0); return }
// no else required because of the early return
// ... straightforward code using new, sane index value

@kptb, what are you building? Is it going to be part of a larger tutorial system? Or a standalone one-time setup? The best approach depends very much on what you want out of your final product.

One quick note:
The key = index % 11; needs to be key = index % 12 to return values 0-11 in an octave of half steps.

And some general thoughts on one way to tackle this:

I’d start by making an array with an entry for each pixel containing the number of the note associated with that pixel. And as @sorceror suggests, -1 for pixels without an associated note. This approach would let you have multiple lit pixels per note if you wanted, as well as making it easy to skip pixels as needed.

I’d use MIDI numbers for the notes, because that would make it easier to enter data from a MIDI keyboard or recording as the project expands. (MIDI note 21 is the bottom A on an 88 key piano keyboard and note 108 the top C)

Then, I’d allocate a 12 slot array to hold “on” notes, and write a couple of functions to use in beforeRender(). One that let you set which notes were currently on, and a handy “turn off all that is on” function. (12 “on” slots should give you enough notes for even the most complex two handed jazz and cluster chords.)

In render() then, you’d just look at the pixel index, get the note associated with it, and see if that note was in the currently “on” list to decide whether or not to light the pixel.

1 Like

Thanks both!

It’s TBA at the moment in terms of output. Trying to have some scale companion for the kids and myself as I re-acquiant, and to map the NI VST controls without owning an NI light guide controller.

I have ordered a set of lights that should be ‘spot on’ - 74/m x2m. Suspect I will pause till those arrive.

Augh! Fencepost error :relaxed: