Chameleon Lamp: the Color Stealing Lamp

by optimus103733 in Circuits > Sensors

1365 Views, 21 Favorites, 0 Comments

Chameleon Lamp: the Color Stealing Lamp

LAMP.jpg
IMG20260323164503.jpg
IMG20260323164732.jpg
IMG20260323164411.jpg

​This is a completely wireless, battery-powered ambient light with a hidden digital "eye." The eye is an SC34725 RGB color sensor.By placing any colorful object like a magazine cover, a red bottle cap, a green leaf against the color sensor, the lamp reads the RGB values of that object and morphs its 60 addressable LEDs to match the color​.

To keep the design neat and simple I didn't want many clunky plastic buttons ruining the aesthetic. Instead, the entire lamp is controlled by invisible capacitive touch sensors made from aluminum foils hidden right on the outside of the enclosure. It has one power button to power on and off the lamp

​With a simple series of taps and holds, you can capture and lock the colors, switch to a "Live Continuous Scan" mode that shifts colors as you slide the lamp across your desk, or cycle through 10 dynamic animations that react to the colors you've just "stolen."

Here is the lamp in action

​In this Instructable, I’ll walk you through the complete build from scratch.

Supplies

1000332440.jpg
1000332438.jpg
1000332434 (2).jpg
1000332444.jpg


​ESP32 Development Board

​TCS34725 RGB Color Sensor:Ats as the "eye" of the lamp.

​WS2812B Addressable LED Strip: 5V strip with 60 LEDs version

​2x 18650 Lithium-Ion Batteries: makes the lamp completely wireless.

​18650 Dual Battery Holder: wired in parallel

​TP4056 Battery Charging Module (Type-C)

​MT3608 DC-DC Step-Up Boost Converter: Crucial for bumping the 3.7V battery power up to a steady 5.0V for the LEDs.

​A standard on/off switch for the main power.

​Enclosure Material: This is up to you, You can 3D print a housing, build one from wood, or upcycle an existing box. Here i used some scrap multi wood and ply wood and glass to make the enclosure

​Clear Glass i used 3mm clear glass 200*100mm 2X

200*96 mm-2X

96*96 mm-1X

​Light-Diffusing Window Film: Often sold as "frosted privacy film." This blurs the individual LEDs into a smooth light.

​B7000 Adhesive: A flexible, high quality glue Perfect for sealing our glass.

​multi wood to make the bottom box part i used 8 mm thick multiwood

100*100mm-2X

34*100mm-2X

34*86mm-2X

Plywood to make the four pillers i used 12 mm plywood of size 200*12mm-4X

​Soldering iron

stranded wire (silicone coated is best for flexibility)

The Breadboard Prototype

1000332450.jpg

The Breadboard Prototype

​Before we heat up the soldering iron or start mixing adhesives, we need to prove that our "brain" and our "eyes" can actually talk to each other. Prototyping on a breadboard allows us to test the color sensor, and diy touch sensor


​Wire the I2C Color Sensor (The Eyes)

The TCS34725 uses the I2C protocol, which requires four wires:

​Connect the sensor's VIN to the ESP32's 3.3V pin. (Do not use 5V, or you will damage the sensor!)

​Connect GND to ESP32 GND.

​Connect SDA (Data) to ESP32 GPIO 21.

​Connect SCL (Clock) to ESP32 GPIO 22.

​Wire the LED Strip (The Output)

​Connect the LED strip's 5V wire to the ESP32's VIN (or VBUS) pin.

​Connect the GND wire to ESP32 GND.

​Connect the DIN (Data In) wire to ESP32 GPIO 13.

Power delivery

Battery to TP 4056 Vin and GND

Out from the TP4056 to Vin and GND of Voltage Booster

Voltage booster output to LED strip and ESP32 5 volt pin (adjust the voltage booster to 5 volt before connecting ESP32)

Touch Sensor

You don't need to solder the aluminum foil just yet.

​Push a long jumper wire into GPIO 4 (The Boss) and another into GPIO 27 (The Entertainer)

​When it is time to test the touch functions, you can simply pinch the bare metal tips of these jumper wires with your fingers. Your body acts as the capacitor!

Constructing the Glass Box

1000332455.jpg
1000332457.jpg
1000332460.jpg
1000332491.jpg


​To give the Chameleon Lamp a sleek, premium look, I decided to build the main tower out of actual glass rather than plastic. To do this without visible brackets, I used an internal wooden skeleton and B7000 adhesive.

First, cut four identical strips of plywood to act as the internal corner pillars. I used 12 mm thick plywood so the final dimension is 12×12×200mm

​ Cut these wooden pillars exactly 4mm shorter than your glass panels! When you assemble the tower, align the bottom of the wood flush with the bottom of the glass. This leaves a 4mm recess at the very top of the tower, creating a perfect little "shelf" so your top glass piece can drop right in and sit flush later.

​Take your B7000 glue and run a generous, squiggly bead down the entire length of the wooden piller

Since glass is heavy and non-porous, you need a strong bond. B7000 works best when used like a contact adhesive.

​Do not press the glass on immediately! After applying the squiggly bead of glue, let it sit exposed to the air for a couple of minutes. This allows the solvents to evaporate slightly, making the glue highly "tacky" so it grabs the glass instantly.

​Carefully press your first pane of glass against the tacky wooden strip.

​Don't try to hold this together by hand in the air. Use a flat table and scrap pieces of wood or MDF to act as a temporary 90-degree jig. Laying the glass flat against a support board ensures that your corners dry perfectly square.

​(see pictures)

It takes 24 to 48 hours to fully cure, and it requires constant pressure during that time. Traditional heavy-duty woodworking clamps will shatter the glass.

​The Solution: Rubber bands! Once all four sides of your glass box are assembled around the wooden pillars, stretch multiple rubber bands completely around the tower.

​Space them out evenly from top to bottom. The rubber bands provide the exact amount of gentle, continuous inward pressure needed to squeeze the glass tightly against the glued wooden pillars without breaking anything.

Electronics

1000332908.jpg
IMG_20260317_152952.jpg
1000333310.jpg
1000333319.jpg
1000332881.jpg
1000334676.jpg
1000334957.jpg
1000332948.jpg
1000333289.jpg
1000334684.jpg

Crafting the Base Box & Electronics Layout

​With the glass tower curing, it is time to build the foundation of the lamp. Because space is tight and we need to avoid electrical noise, the layout of the bottom box needs to be very deliberate. I used multiwood to craft the base and organized the components into two main sections: a flat floor for the heavy batteries and the brain, and a vertical wall for the power modules and switches

The Base Plate & The Sensor Cutout

For the lamp to accurately sense colors, the TCS34725 sensor must sit 3-10mm from the bottom surface, and the bottom panel should not let any ambient light to the sensor which will cause the sensor to read wrong value

​First, cut the main bottom panel for your electronics box.

​Find the center and trace the exact dimensions of your sensor board. Carefully cut this square hole do not go all the way through just half the depth is enough .Make this cut just snug enough so the sensor board drops in perfectly. Placing the sensor under the bottom flat panel prevents ambient room light from leaking in and washing out color scans!

​Solder four wires to the sensor: VIN (3.3V), GND, SDA, and SCL.

​Don't forget the Hardware Bridge: Solder a tiny bridge connecting the LED pin to the 3.3V pin on the sensor board. This forces the sensor's white LED to stay permanently on so it can illuminate the objects you scan.

​Drop the sensor into the square cutout and route the wires up into the main box compartment..

The Power Sub-Assembly (The Vertical Wall)

To keep the footprint small and ensure the USB charging port is accessible from the outside, I super glued the power delivery system on the vertical wall of the box. Solder the nessary connection before gluing the boards on hte wall

The TP4056 charging module features tiny onboard surface-mount LEDs to indicate when the battery is actively charging and when it is fully charged. Because this module is sealed completely inside the bottom electronics box, these vital status lights must be extended to the exterior of the enclosure.

​Fine wires are carefully soldered directly to the solder pads of the existing indicator LEDs on the TP4056 board. Standard through-hole LEDs are then soldered to the opposite ends of these extended wires. Two small holes are drilled through the vertical wall of the white base enclosure, positioned near the USB charging port cutout. The newly wired LEDs are pushed through these holes from the inside so the bulbs sit securely in the exterior wall.

​When the decorative wood-grain vinyl wrap is applied to the base in the later finishing stages, tiny holes are carefully pierced through the film to expose just the tips of the LED bulbs. This ensures the battery charging status can always be monitored at a glance without compromising the clean, sealed look of the lamp's exterior.

​Mount your TP4056 Type-C Charger and your MT3608 Boost Converter flat against this wall. Ensure the USB-C port hangs just over the edge so you can plug a cable into it from the outside.

​Solder the OUT+ and OUT- pads from the TP4056 directly to the VIN+ and VIN- pads of the MT3608.

​A physical on/off switch is added to the output +ve wire of the MT3608

Mounting the Heavy Hardware

With the walls and base prepped, it's time to lock the big components down.

​Glue the dual 18650 battery holder flat onto the main base plate to keep the lamp's center of gravity low so it doesn't tip over.

​Mount the ESP32 microcontroller on female breg strip connector the breg connector leg is then pressed inside the multiwood and glued using super glue for better strength

​Glue the vertical power wall to the back edge of the base plate, creating an "L" shape.

​Route the red and black wires from the battery holder to the B+ and B- pads on the TP4056 charger on the vertical wall.

Wiring the Brains (Direct Soldering)

To save vertical space and ensure permanent connections, skip the standard breadboard jumper wires and solder everything directly to the ESP32 pins.

​Take your multimeter, turn on the battery power, and turn the tiny brass screw on the MT3608 until it outputs exactly 5.0V. (Do not skip this, or you will fry the ESP32!)

​Solder the 5V VOUT+ from the MT3608 directly to a switch and then to the VIN of ESP32 and the VOUT- directly to GND pins on the ESP32.

​Solder your sensor's wires to the ESP32: VIN to 3.3V, GND to GND, SDA to GPIO 21, and SCL to GPIO 22.

​Finally, solder the data wires (red, green, blue) that will eventually go to your LED strip and touch sensors. Try keeping the wires short and clean it will be better to solder the wires to the box than leaving them free

The LED Column & Base Assembly

IMG_20260327_085312.jpg
IMG_20260327_085107.jpg
IMG_20260327_084829.jpg
1000332939.jpg
1000334638.jpg
1000333331.jpg
1000334645.jpg
IMG20260306204521.jpg


​With the electronics wired, the next phase is building the central light core and sealing the base unit so the glass tower can mount to it seamlessly.

Creating the Notched Lid

The top cover of the electronics box must act as a structural lid while also locking the glass tower in place.

​Cut a square multiwood to serve as the top cover.

​Cut square notches out of all four corners of this lid assembly. Measure the internal wooden pillars of your glass tower—these notches must match those dimensions perfectly.

​Drill a small wire-routing hole near the center of the plate.

Wrapping the Central LED Core

To distribute the light evenly through the frosted glass and avoid visible hotspots, the LEDs are mounted in a dense spiral.

​Cut a PVC pipe to act as the central column. The pipe should be slightly shorter than the overall height of your glass tower.

​Take the WS2812B LED strip and peel back the adhesive. Starting from the bottom, wrap the strip tightly around the PVC pipe in a continuous, ascending spiral. Keep the gaps between the coils even.

​Route the three wire leads (5V, Data, GND) from the bottom of the LED strip down through the center hole in the lid.

​Secure the bottom of the PVC pipe to the center of the lid using super glue

Sealing the Electronics Base

​Solder the three LED wires to the pre-wired connections inside the bottom electronics enclosure (5V to the MT3608 output, GND to common ground, and Data to ESP32 GPIO 13).

​Place the assembled lid over the electronics box.

​Secure the lid to the walls of the base box using four screws. Drive one screw into each corner, positioning them just outside the notched cutouts so they clear the glass tower later.

Mounting the Glass Tower

I used 4 screws to seal the box after that add good amount of b700 glue along the notches and the bottom of the glass box.​Take the cured, frosted glass tower and slide it down over the central PVC LED column.9see pictures)

​Align the four internal wooden pillars of the glass tower with the four corner notches in the base lid.

​Slide the tower all the way down. The wooden pillars will slot directly into the notches, structurally locking the tower so it cannot twist or shift. The bottom edge of the glass will rest perfectly flush against the outer rim of the wooden base box.as the last step place the top glass piece, it will sit neatly in the gap on top and will be supported by the 4 pillers from inside

Code

#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <FastLED.h>
#include <math.h>

// --- PIN CONFIGURATION ---
#define LED_PIN 13
#define INDICATOR_LED 2
#define TOUCH_BOSS 4
#define TOUCH_ENT 27
#define NUM_LEDS 60
#define BRIGHTNESS 100
#define TOUCH_LIMIT 40

Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);
CRGB leds[NUM_LEDS];

// --- STATE VARIABLES ---
CRGB activeColor = CRGB::White;
int currentMode = 0;
uint8_t gHue = 0;
bool continuousScan = false;

// --- TOUCH 1 (BOSS) VARIABLES ---
bool t1_wasActive = false;
bool t1_isHolding = false;
unsigned long t1_downTime = 0;
unsigned long t1_upTime = 0;
int t1_tapCount = 0;

// --- TOUCH 2 (ENTERTAINER) VARIABLES ---
bool t2_wasActive = false;
unsigned long t2_downTime = 0;
unsigned long t2_upTime = 0;
int t2_tapCount = 0;

void setup() {
Serial.begin(115200);
Wire.begin();
pinMode(INDICATOR_LED, OUTPUT);
digitalWrite(INDICATOR_LED, LOW);

FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);

// Power Governor to keep your MT3608 booster safe
FastLED.setMaxPowerInVoltsAndMilliamps(5, 950);

if (tcs.begin()) {
Serial.println("SYSTEM BOOT: Defaulting to White");
fill_solid(leds, NUM_LEDS, activeColor);
FastLED.show();
} else {
Serial.println("ERROR: Sensor missing.");
}
}

void loop() {
bool t1_active = (touchRead(TOUCH_BOSS) < TOUCH_LIMIT);
bool t2_active = (touchRead(TOUCH_ENT) < TOUCH_LIMIT);

// ==========================================
// TOUCH 1: THE BOSS (Scan Lock & Live Mode)
// ==========================================

if (t1_active && !t1_wasActive) {
t1_downTime = millis();
t1_tapCount++;
}

if (t1_active && t1_wasActive) {
if (millis() - t1_downTime > 500) {
// --- LONG PRESS DETECTED ---
t1_isHolding = true;
t1_tapCount = 0;
continuousScan = false;
digitalWrite(INDICATOR_LED, HIGH);
runScanner();
}
}

if (!t1_active && t1_wasActive) {
t1_upTime = millis();
if (t1_isHolding) {
// --- RELEASED FROM LONG PRESS ---
t1_isHolding = false;
digitalWrite(INDICATOR_LED, LOW);
Serial.println("COLOR LOCKED");
}
}

if (!t1_active && !t1_wasActive) {
if (t1_tapCount > 0 && (millis() - t1_upTime > 400)) {
if (t1_tapCount == 2) {
// --- DOUBLE TAP CONFIRMED ---
continuousScan = !continuousScan;
currentMode = 0; // Snap to solid mode so we can clearly see the live scan

if (continuousScan) {
digitalWrite(INDICATOR_LED, HIGH);
Serial.println("LIVE SCAN: ON");

// NOTIFICATION: BLINK TWICE (Entering Live Mode)
for(int i=0; i<2; i++) {
fill_solid(leds, NUM_LEDS, CRGB::White);
FastLED.show();
delay(120);
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
delay(120);
}
} else {
digitalWrite(INDICATOR_LED, LOW);
Serial.println("LIVE SCAN: OFF (Locked)");

// NOTIFICATION: BLINK ONCE (Exiting Live Mode)
fill_solid(leds, NUM_LEDS, activeColor);
FastLED.show();
delay(200);
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
delay(100);
}
}
t1_tapCount = 0;
}
}
t1_wasActive = t1_active;

// ==========================================
// TOUCH 2: THE ENTERTAINER (Animations)
// ==========================================

if (t2_active && !t2_wasActive) {
t2_downTime = millis();
t2_tapCount++;
}
if (!t2_active && t2_wasActive) {
t2_upTime = millis();
}
if (!t2_active && !t2_wasActive) {
if (t2_tapCount > 0 && (millis() - t2_upTime > 400)) {
if (t2_tapCount == 2) {
// --- DOUBLE TAP CONFIRMED ---
currentMode = (currentMode + 1) % 11;
fill_solid(leds, NUM_LEDS, CRGB::Black); // Flash black to confirm tap
FastLED.show();
delay(50);
Serial.print("ANIMATION MODE: "); Serial.println(currentMode);
}
t2_tapCount = 0;
}
}
t2_wasActive = t2_active;

// ==========================================
// LIVE BACKGROUND SCANNER
// ==========================================
if (continuousScan) {
// Updates every 100ms so the strip animations remain smooth
EVERY_N_MILLISECONDS(100) {
runScanner();
}
}

// ==========================================
// RENDER OUTPUT
// ==========================================
runAnimations();
FastLED.show();
delay(10); // Very brief delay to keep the touch sensing hyper-responsive
}

// ==========================================
// THE COLOR ACCURACY ENGINE
// ==========================================
void runScanner() {
uint16_t r, g, b, c;
tcs.getRawData(&r, &g, &b, &c);

if (c > 50) {
float r_ratio = (float)r / c;
float g_ratio = (float)g / c;
float b_ratio = (float)b / c;

// 1. YOUR CUSTOM WHITE-BALANCE MULTIPLIERS (Based on your raw data)
float rf = r_ratio * 1.00;
float gf = g_ratio * 1.21;
float bf = b_ratio * 1.88;

// 2. SATURATION BOOSTER (Stripping the white glare)
float min_val = min(rf, min(gf, bf));
rf = (rf - min_val * 0.85) * 255.0;
gf = (gf - min_val * 0.85) * 255.0;
bf = (bf - min_val * 0.85) * 255.0;

// 3. BRIGHTNESS NORMALIZATION
float max_val = max(rf, max(gf, bf));
if (max_val > 0) {
float scale = 255.0 / max_val;
rf *= scale;
gf *= scale;
bf *= scale;
}

// 4. GAMMA CORRECTION (Mapping to human eye curves)
int final_r = 255.0 * pow(rf / 255.0, 2.5);
int final_g = 255.0 * pow(gf / 255.0, 2.5);
int final_b = 255.0 * pow(bf / 255.0, 2.5);

// 5. SMOOTH BLENDING
activeColor.r = (activeColor.r * 0.90) + (constrain(final_r, 0, 255) * 0.10);
activeColor.g = (activeColor.g * 0.90) + (constrain(final_g, 0, 255) * 0.10);
activeColor.b = (activeColor.b * 0.90) + (constrain(final_b, 0, 255) * 0.10);
}
}

// ==========================================
// 10-MODE ANIMATION SUITE
// ==========================================
void runAnimations() {
EVERY_N_MILLISECONDS(20) { gHue++; }

switch (currentMode) {
case 0: fill_solid(leds, NUM_LEDS, activeColor); break;
case 1: fill_rainbow(leds, NUM_LEDS, gHue, 7); break;
case 2:
fadeToBlackBy(leds, NUM_LEDS, 10);
if(random8() < 40) leds[random16(NUM_LEDS)] += CHSV(gHue + random8(64), 200, 255);
break;
case 3:
fill_solid(leds, NUM_LEDS, activeColor);
FastLED.setBrightness(beatsin8(15, 30, BRIGHTNESS));
break;
case 4:
fadeToBlackBy(leds, NUM_LEDS, 30);
leds[beatsin16(25, 0, NUM_LEDS-1)] = activeColor;
break;
case 5:
fadeToBlackBy(leds, NUM_LEDS, 40);
for(int i=0; i<NUM_LEDS; i++) if((i+(millis()/100))%5==0) leds[i]=activeColor;
break;
case 6:
fadeToBlackBy(leds, NUM_LEDS, 20);
for(int i=0; i<8; i++) leds[beatsin16(i+7, 0, NUM_LEDS-1)] |= CHSV(gHue+(i*32), 200, 255);
break;
case 7:
for(int i=0; i<NUM_LEDS; i++) leds[i] = ColorFromPalette(PartyColors_p, gHue+(i*2), beatsin8(62,64,255));
break;
case 8:
fill_solid(leds, NUM_LEDS, CRGB(0,0,30));
if(random8()<20) leds[random16(NUM_LEDS)]=CRGB::White;
fadeToBlackBy(leds, NUM_LEDS, 10);
break;
case 9:
fadeToBlackBy(leds, NUM_LEDS, 20);
leds[beatsin16(25, 0, NUM_LEDS-1)] = CRGB::Red;
break;
case 10:
fadeToBlackBy(leds, NUM_LEDS, 50);
for(int i=0; i<NUM_LEDS; i++) if(i%3==(millis()/100)%3) leds[i]=activeColor;
break;
}

// Ensure the brightness restores perfectly if you cycle out of breathing mode
if (currentMode != 3) FastLED.setBrightness(BRIGHTNESS);
}

How the Code Works


The code requires two main libraries to function: FastLED to control the WS2812B addressable strip, and Adafruit_TCS34725 to communicate with the I2C color sensor.In the setup phase, the ESP32 initializes the sensor and the LED strip. A crucial detail here is the setMaxPowerInVoltsAndMilliamps function. This caps the power draw of the LEDs to prevent the 5V boost converter from overloading and shutting down when trying to display bright white across all 60 pixels.

The main loop is dedicated to reading the two capacitive touch switches using the ESP32's built-in touchRead functions. Custom timing logic acts as a debounce, distinguishing between a sustained hold and a quick double-tap. The primary washer controls the scanning behavior. Pressing and holding it triggers a single color read. Double-tapping it toggles continuous "Live Mode," allowing the lamp to actively update its color as you slide it across different surfaces. The secondary washer acts as an animation selector, where a double-tap cycles the code through ten different lighting patterns.

When the scanner is activated, the runScanner() function retrieves the raw red, green, blue, and clear channel data from the TCS34725. Because the glass panels and frosted film inherently alter the perceived color of the light, the raw data must be mathematically calibrated. The code calculates the ratio of each color, applies specific multipliers to fix the sensor's natural bias, and aggressively strips away white light glare to boost overall saturation. It then applies a gamma correction curve so the resulting LED output matches human visual perception. To prevent harsh, jarring flashes, the math smoothly interpolates the transition from the old color to the newly scanned color.

The runAnimations() function manages the visual output using a switch-case structure. Mode 0 displays the solid scanned color, while the other modes leverage FastLED's built-in math functions to generate breathing effects, moving dots, or static twinkling. Because the animations reference the activeColor variable, the effects dynamically adapt their color palettes based on whatever object the sensor is currently reading.

There is an indication LED on GPIO2 which i removed from the final version,but its still in the code if you want you can add an led to GPIO2

Capacitive Touch Sensors and Exterior Vinyl Wrap

IMG_20260327_085225.jpg
IMG_20260327_085208.jpg
IMG_20260327_085035.jpg
IMG20260323164503.jpg


Before applying the final exterior finish, the capacitive touch controls must be installed. Instead of using bulky hardware, simple pieces of aluminum foil act as the touch sensors. The foil is cut to size and attached flat against the outside of the base enclosure. A wire is securely connected to each piece of foil, and these wires are routed through small holes into the electronics compartment to connect to the designated touch pins on the ESP32.

Once the foil sensors are in place, the decorative wood-grain vinyl film is applied to the exterior to hide the internal construction. Precise strips of this film are cut and applied vertically along the four outside corners of the glass tower. This frames the frosted glass windows while completely concealing the internal plywood pillars and the dried B7000 adhesive seams.

The entire exterior of the bottom electronics box is then wrapped in the same wood-grain vinyl. This wrap is applied directly over the aluminum foil. Because the ESP32's capacitive touch pins can detect changes in the electromagnetic field through thin materials, the vinyl completely hides the foil from view while leaving the touch controls fully functional. This creates an invisible, seamless interface on the base of the lamp while covering the raw edges of the white rigid board and mounting screws

Conclusion and Future Modifications

IMG20260323164732.jpg
IMG20260323164912.jpg
IMG20260323170030.jpg


With the code uploaded and the exterior wrapped, the Chameleon Lamp is fully operational. This project combines structural assembly, simple electronics, and microcontroller programming into a functional piece of interactive lighting.

Because the system is modular, the core design can be easily adapted. The dimensions of the internal pillars and the base can be scaled to accommodate much larger glass panels or a higher-capacity battery pack for extended wireless runtime. Different exterior vinyl wraps or tinted glass films can be used to change the lamp's default aesthetic to match different room environments.

The software leaves room for expansion as well. Since the ESP32 has onboard Wi-Fi and Bluetooth capabilities, the code can be modified to allow the lamp to connect to a smart home network, sync with other ambient lighting setups, or be controlled via a smartphone app when the physical color-scanning feature is not being used.

When building this, the most critical details to remember are keeping the bottom color sensor perfectly flush to block out room light, maintaining even pressure on the glass panels while the B7000 cures, and always verifying the 5.0V output on the boost converter before connecting the ESP32.