Node-red has changed everything about how I network components of a project together. It’s a node interface for connecting technologies together. If there is an API provided for a service, chances are someone has written a node for it. Beyond use for show control I also use it to manage smart home features. It’s integrated with Alexa, RFID readers, and my Stream Deck to change me lighting and play music. Most often I use it to connect things together using UDP. For example an RFID reader connected to arduino will read in a code and send it up to Node-red. Then I’ll write a custom node in node.js to handle the logic, and then pass that to whatever it needs to control like video on raspberry pi, game features in Unity, Hue lights, or DMX. The former two have simple code written by me in Python of C# to receive the info, and the latter two have their own nodes already build for input making it super easy. Barely an inconvenience!


A good start for setting up Node-red: https://nodered.org/about/

My arduino escape room github branch: https://github.com/crydrk/arduinoEscapeRoom/tree/main/WifiStatues

And the raspberry pi side, though lots of this is bespoke and hard coded for my projects and should be used for example only. It also doesn’t include any of the setup or installations to run out of the box: https://github.com/crydrk/raspberryPiEscapeRoom

(Additional example javascript and documentation at the bottom of this page)


Tiki Themed Bar and RFID Triggered Show Control

This bar was for a tiki bar themed D&D session. I used images from Heroforge to animate about 20 minutes of footage to represent ‘memory pearls’. They were snow globes with rfid tags inside of them that would trigger lighting effects and videos. In-story, they were investigating an alternate timeline version of their group and learning the story by solving puzzles and activities delivered by NPCs. The videos were stored on a raspberry pi, and the RFID reader was a wifi enabled Arduino.

Circus Song with Coordinated DMX Lighting

One of my players’ backstories was of leaving the circus. When they eventually found that circus in the campaign, they were greeted by his old ringleader, the financially irresponsible Kervon Sen. I hired a couple dancers to show up secretly and enter at the right time in the song. We never rehearsed (you can even see me at the end looking at the dancers to try to incorporate some of the same movements, heh), and had only communicated about it over email, but I’m really happy about how it came together.

One showed up about half an hour late so I had to stall for time. If you have DM’d before, you may know that it took a lot of concentration to keep the players in the right place while also doing improv activities and communicating over text at the same time. Only my fiancĂ©e knew that they were coming, and she was nervous wondering why I wasn’t starting the show on time, and it was difficult to let her know the details while staying in character and running the game.

The DMX lights were animated in Unity, and connected with DMX through node red to run the show in time with music. I had two motorized RGB spotlights, a polka dot light projector, water light fixture and strobes. It also ran the 9 hue lights throughout the house.

Magic Show

The players came across a sort of fraud magician character who did a magic show. The story presented in the show was a man from the past who froze himself in a block of ice to come forward in time. Later they learned he actually was brought from a future that no longer existed because of a paradox. The campaign dealt heavily in time travel mechanics, and is quite a bit to explain, but no relevant to this post anyway.

This is a followup tech-wise to the circus show. A lot of the same fixtures, animated in Unity. The only real difference this time was the projection at the beginning.

Cthulhu Cultist Song

This was sort of the precursor to all the work with node-red. Only the Hue lights were managed by node-red in this show. Everything else was activated manually.

Twitch Chat Integration

I’ve set up my lights so that my chat can change the color of my lights by typing Hogwarts houses or the word ‘hunt’. Below is an example of someone triggering the hunt light animation during a hunt in the game Phasmophobia. I also have lighting controls on my Stream Deck.

No description available.

More Details

Function is a very useful node in Node-red to parse payload and distribute data.

In the snippet below, I am taking in the data from the arduino through a Udp node and parsing it through the function node. The messages come through in a json-like format, and in this case a string is sent in the payload entry. I’ve hard coded values for a dnd show. Before running any of the matching conditions, I store the received string in a variable global to the flow to keep it from acting on the same code multiple times in a row. One scenario for this is leaving the tag on the reader. Eventually I ought to add that functionality at the arduino level and implement a timer instead. flow.get and .set act as global variables outside of the node.

Next, if an RFID code matches one of the hard coded values, I return an array of payloads. (ex. return [{payload:true}, {payload:”001″}];) Referencing the image above, you will see the first output goes to a Hue light node and the second goes to a udp node to send a message to the pi. Spot 0 in the array pipes to the first output, and spot 1 to the second. This turns down the lights and triggers the video to play all at once. Python, javascript, C++ish, and the Hue API all working together at once with little effort! I love, love, love Node-red.

 var input = msg.payload
 var scene001 = '1171513528';
 var scene002 = '117167528';
 var scene003 = '117155628';
 var scene004 = '10120020228';
 var scene005 = '1017223228';
 var scene006 = '10122218628';
 var scene007 = '101906428';
 var scene009 = '1013216628';
 var scene010 = '10125419928';
 var kill = '7511128128';
 var lastID = flow.get("lastIDTiki");
 if (input == lastID)
     flow.set("lastIDTiki", input);
 if (input == scene001)
     return [{payload:true}, {payload:"001"}];
 if (input == scene002)
     return [{payload:true}, {payload:"002"}];
 if (input == scene003)
     return [{payload:true}, {payload:"003"}];
 if (input == scene004)
     return [{payload:true}, {payload:"004"}];
 if (input == scene005)
     return [{payload:true}, {payload:"005"}];
 if (input == scene006)
     return [{payload:true}, {payload:"006"}];
 if (input == scene007)
     return [{payload:true}, {payload:"007"}];
 if (input == scene009)
     return [{payload:true}, {payload:"009"}];
 if (input == scene010)
     return [{payload:true}, {payload:"010"}];
 if (input == kill)
     return [{payload:true}, {payload:"kill"}];