[Edit: Welcome Hackaday! You might prefer real vacuum tubes; searching for “vacuum tube leds” will turn up more posts about this long-running project. And, yes, I lit up a tube, just for old time’s sake, and have some plans for that huge triode.]
A single (knockoff) Neopixel hovers over a defunct halogen bulb:

The Arduino code comes from stripping down the Hard Drive Platter Mood Light to suit just one Neopixel, with the maximum PWM values favoring the red-blue-purple end of the color wheel:
Pixels[RED].Prime = 3;
Pixels[GREEN].Prime = 5;
Pixels[BLUE].Prime = 7;
printf("Primes: (%d,%d,%d)\r\n",Pixels[RED].Prime,Pixels[GREEN].Prime,Pixels[BLUE].Prime);
Pixels[RED].MaxPWM = 255;
Pixels[GREEN].MaxPWM = 64;
Pixels[BLUE].MaxPWM = 255;
Unlike the Mood Light’s dozen Neopixels jammed into the platter’s hub ring, running one Neopixel at full throttle atop the tube doesn’t overheat the poor controller. In a 22 °C room, PWM 255 white raises the cap’s interior temperature to 35 °C, which looks like a horrific 40 °C/W thermal coefficient if you figure the dissipation at 300 mW = 5 V x 60 mA.
Feeding those parameters into the raised sine wave equation causes the cap to tick along at 27 °C for an average dissipation of 120 mW, which sounds about right:
113 mW = 5 V x (20 + 20 + 5 mA) / 2
The effect is striking in a dark room, but it’s hard to photograph; the halogen capsule inside the bulb resembles a Steampunk glass jellyfish:

That ceramic light socket should stand on a round base with room for the Arduino controller. I think powering it from a wall wart through a USB cable makes sense, with a USB-to-serial converter epoxied inside the box for reprogramming.
It looks pretty good, methinks, should you like that sort of thing.
The Arduino source code as a GitHub gist:
| // Neopixel mood lighting for vacuum tubes | |
| // Ed Nisley – KE4ANU – January 2016 | |
| #include <Adafruit_NeoPixel.h> | |
| //———- | |
| // Pin assignments | |
| const byte PIN_NEO = 6; // DO – data out to first Neopixel | |
| const byte PIN_HEARTBEAT = 13; // DO – Arduino LED | |
| //———- | |
| // Constants | |
| #define UPDATEINTERVAL 25ul | |
| const unsigned long UpdateMS = UPDATEINTERVAL – 1ul; // update LEDs only this many ms apart minus loop() overhead | |
| // number of steps per cycle, before applying prime factors | |
| #define RESOLUTION 100 | |
| // want to randomize the startup a little? | |
| #define RANDOMIZE true | |
| //———- | |
| // Globals | |
| // instantiate the Neopixel buffer array | |
| Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, PIN_NEO, NEO_GRB + NEO_KHZ800); | |
| uint32_t FullWhite = strip.Color(255,255,255); | |
| uint32_t FullOff = strip.Color(0,0,0); | |
| struct pixcolor_t { | |
| byte Prime; | |
| unsigned int NumSteps; | |
| unsigned int Step; | |
| float StepSize; | |
| byte MaxPWM; | |
| }; | |
| unsigned int PlatterSteps; | |
| // colors in each LED | |
| enum pixcolors {RED, GREEN, BLUE, PIXELSIZE}; | |
| struct pixcolor_t Pixels[PIXELSIZE]; // all the data for each pixel color intensity | |
| unsigned long MillisNow; | |
| unsigned long MillisThen; | |
| //– Figure PWM based on current state | |
| byte StepColor(byte Color, float Phi) { | |
| byte Value; | |
| Value = (Pixels[Color].MaxPWM / 2.0) * (1.0 + sin(Pixels[Color].Step * Pixels[Color].StepSize + Phi)); | |
| // Value = (Value) ? Value : Pixels[Color].MaxPWM; // flash at dimmest points | |
| return Value; | |
| } | |
| //– Helper routine for printf() | |
| int s_putc(char c, FILE *t) { | |
| Serial.write(c); | |
| } | |
| //—————— | |
| // Set the mood | |
| void setup() { | |
| pinMode(PIN_HEARTBEAT,OUTPUT); | |
| digitalWrite(PIN_HEARTBEAT,LOW); // show we arrived | |
| Serial.begin(57600); | |
| fdevopen(&s_putc,0); // set up serial output for printf() | |
| printf("Vacuum Tube Mood Light with Neopixels\r\nEd Nisley – KE4ZNU – January 2016\r\n"); | |
| /// set up Neopixels | |
| strip.begin(); | |
| strip.show(); | |
| // lamp test: a brilliant white dot | |
| printf("Lamp test: flash white\r\n"); | |
| for (byte i=0; i<3 ; i++) { | |
| for (int j=0; j < strip.numPixels(); j++) { // fill LEDs with white | |
| strip.setPixelColor(j,FullWhite); | |
| } | |
| strip.show(); | |
| delay(500); | |
| for (int j=0; j < strip.numPixels(); j++) { // fill LEDs with black | |
| strip.setPixelColor(j,FullOff); | |
| } | |
| strip.show(); | |
| delay(500); | |
| } | |
| // set up the color generators | |
| MillisNow = MillisThen = millis(); | |
| if (RANDOMIZE) | |
| randomSeed(MillisNow + analogRead(7)); | |
| else | |
| printf("Start not randomized\r\n"); | |
| printf("First random number: %ld\r\n",random(10)); | |
| Pixels[RED].Prime = 3; | |
| Pixels[GREEN].Prime = 5; | |
| Pixels[BLUE].Prime = 7; | |
| printf("Primes: (%d,%d,%d)\r\n",Pixels[RED].Prime,Pixels[GREEN].Prime,Pixels[BLUE].Prime); | |
| Pixels[RED].MaxPWM = 255; | |
| Pixels[GREEN].MaxPWM = 64; | |
| Pixels[BLUE].MaxPWM = 255; | |
| for (byte c=0; c < PIXELSIZE; c++) { | |
| Pixels[c].NumSteps = RESOLUTION * (unsigned int) Pixels[c].Prime; | |
| Pixels[c].Step = (RANDOMIZE) ? random(Pixels[c].NumSteps) : (3*Pixels[c].NumSteps)/4; | |
| Pixels[c].StepSize = TWO_PI / Pixels[c].NumSteps; // in radians per step | |
| printf("c: %d Steps: %d Init: %d",c,Pixels[c].NumSteps,Pixels[c].Step); | |
| printf(" PWM: %d\r\n",Pixels[c].MaxPWM); | |
| } | |
| } | |
| //—————— | |
| // Run the mood | |
| void loop() { | |
| MillisNow = millis(); | |
| if ((MillisNow – MillisThen) > UpdateMS) { | |
| digitalWrite(PIN_HEARTBEAT,HIGH); | |
| for (byte c=0; c < PIXELSIZE; c++) { // step to next increment in each color | |
| if (++Pixels[c].Step >= Pixels[c].NumSteps) { | |
| Pixels[c].Step = 0; | |
| printf("Cycle %d steps %d at %8ld delta %ld ms\r\n",c,Pixels[c].NumSteps,MillisNow,(MillisNow – MillisThen)); | |
| } | |
| } | |
| byte Value[PIXELSIZE]; | |
| for (byte c=0; c < PIXELSIZE; c++) { // … for each color | |
| Value[c] = StepColor(c,0.0); // figure new PWM value | |
| // Value[c] = (c == RED && Value[c] == 0) ? Pixels[c].MaxPWM : Value[c]; // flash highlight for tracking | |
| } | |
| uint32_t UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE]); | |
| for (int j=0; j < strip.numPixels(); j++) { // fill LEDs with color | |
| strip.setPixelColor(j,UniColor); | |
| } | |
| strip.show(); | |
| MillisThen = MillisNow; | |
| digitalWrite(PIN_HEARTBEAT,LOW); | |
| } | |
| } |




























