LumeBloom: a Lotus Flower-Shaped Lamp That Blooms for Mindful Lighting

by brookejahng in Living > Health

519 Views, 9 Favorites, 0 Comments

LumeBloom: a Lotus Flower-Shaped Lamp That Blooms for Mindful Lighting

LumeBloom Final

📘 Introduction

LumeBloom is a lotus flower-shaped mood lamp designed to strike a balance between leisure and learning — from a student’s perspective.

In recent years, mental health has become an increasingly important focus, especially for students managing the demands of academic life. As someone who often feels overwhelmed by the challenge of finding balance, I wanted to contribute by designing something personal and practical. This lamp began as part of a modular desktop system inspired by the Pomodoro Technique — alternating 25-minute focus sessions with 5-minute rest periods to support better time management and mental breathing room.

While developing the concept, I was drawn to the symbolism of the lotus flower, a central image in Buddhist philosophy. The lotus rises from muddy waters to bloom in stillness, representing calm, clarity, and growth. These values shaped the lamp’s design — from its slow blooming motion to its warm light and lotus flower scent — creating a quiet, calming presence in any space.

LumeBloom opens like a flower, releasing light and aroma in a way that encourages pause, breath, and emotional reset. It’s both a functional object and a mindful ritual: a small moment of intention to start or end the day.

Though originally envisioned as one module in a larger desk system for well-being, LumeBloom evolved into an individual piece to represent balance and calmness.

Supplies

Materials Used

  1. Arduino Uno
  2. Servo motor
  3. LED strip
  4. Jumper wires
  5. Metal stick (hollow)
  6. 3D Printer
  7. Laser-cutter
  8. Korean Traditional paper
  9. Essential oil
  10. Wood base
  11. Hot glue or super glue
  12. Heat gun
  13. USB cable (for power)

Sketch Your Idea

Screenshot 2025-07-19 at 5.25.18 PM.png
  1. Sketch the blooming shape of the lamp — imagine a lotus flower bud opening.
  2. Decide on petal layout: how many, what size, and how they open.
  3. Think about how it will move (servo + linkage).
  4. Choose where to place the LEDs and scent diffuser.
I drew inspiration from natural blooming forms — not just flowers, but unfolding leaves, seeds, and even paper sculptures. I wanted something slow and deliberate, almost meditative in its movement. Early sketches focused on exaggerated petal arcs and centralized movement. I prioritized symmetry and simplicity to keep the final mechanism easy to build and reproduce.


3D Sketching + Printing

IMG_0282.png
image (1).png
IMG_0310.jpeg
IMG_0292.jpeg
image.png

While brainstorming and planning ideas for this mod, I learned how to create it with a 3D printing program using Fusion and different 3D printers.

To start the project, I focused on brainstorming possibilities for building a blooming flower light using various materials. I explored different mechanical approaches and design options that would create the most realistic and elegant blooming motion. After developing a rough sketch where I mapped out the overall structure, petal arrangement, and movement mechanics, I translated my 2D concept into a comprehensive 3D model using Fusion. This transition from paper to digital allowed me to fine-tune proportions, test different petal configurations, and visualize how the internal components would interact with the exterior design before moving to physical prototyping.

Mechanical components, such as gear parts, were constructed with 3D programming software. These gears were taken from a free gear generator to ensure the precision and operation of the blooming mechanism, powered by a servo motor. This was a vital stage in constructing the lamp's moving parts. After experimenting with various gear ratios and tooth configurations, we found the optimal design that provided the precise mechanical advantage needed for the delicate movement. This careful attention to the gear system was essential for creating the natural, fluid blooming effect that mimics the gentle unfurling of a real lotus flower.


Note: I tried different petal shapes ranging with different wideness and thickness. Narrower and thinner petals were more effective in closings, ultimately.


LASER CUTTING

image (2).png
I initially tried to laser cut the petals from wood, but the material didn’t give me the flexibility or appearance I wanted for the top.


Instead, I used the laser-cut wood as the bottom platform of the lamp. It now serves as the structural base, holding the servo motor and providing support for the blooming mechanism. The wooden base also adds a natural and warm contrast to the glowing petals, grounding the whole piece visually and physically.

SERVO MOTORS

servo motor

This involved exploring how servo motors, controlled through Arduino programming, could create the blooming motion.

During this step, I faced challenges with getting the servo to move smoothly across the intended range. After testing different degrees of rotation (from 20° to 80°), I found an effective spot that created the most natural blooming motion. The servo needed to be powerful enough to lift all the petals simultaneously while maintaining precision.

For a better understanding and to provide a guideline for maximizing the potential of the servomotor, I created a flowchart.

//this is the code used for testing the servo motor

#include <Servo.h>

Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 20; // variable to store the servo position

void setup() {
myservo.attach(6); // attaches the servo on pin 9 to the servo object
}

void loop() {
for (pos = 20; pos <= 80; pos += 1) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15 ms for the servo to reach the position
}
for (pos = 80; pos >= 20; pos -= 1) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15 ms for the servo to reach the position
}

RGB LED STRIPS

The gentle glow of RGB LED strips adds comfort, and the servo motor-driven blooming action offers a calming visual reminder of growth. Moreover, the gear and pinion mechanism was made using 3D printing so that no extra assembly was required. The petals are crafted using traditional handmade Korean paper to offer a unique, handcrafted touch. This light is helpful for everyone, but it works especially well for people who want comfort and improved mental health. Just flicking on the lamp lets the lotus flower bloom, which is soothing and especially helpful while studying or under stress.

This is the code:

//code for testing LED colors and effects

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 6

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 60

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)


// setup() function -- runs once at startup --------------------------------

void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.

strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
}


// loop() function -- runs repeatedly as long as board is on ---------------

void loop() {
// Fill along the length of the strip in various colors...
colorWipe(strip.Color(255, 0, 0), 50); // Red
colorWipe(strip.Color( 0, 255, 0), 50); // Green
colorWipe(strip.Color( 0, 0, 255), 50); // Blue

// Do a theater marquee effect in various colors...
theaterChase(strip.Color(127, 127, 127), 50); // White, half brightness
theaterChase(strip.Color(127, 0, 0), 50); // Red, half brightness
theaterChase(strip.Color( 0, 0, 127), 50); // Blue, half brightness

rainbow(10); // Flowing rainbow cycle along the whole strip
theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
}


// Some functions of our own for creating animated effects -----------------

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}

// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}

// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop:
for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
strip.rainbow(firstPixelHue);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}

// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}



//** with more LEDs, the servo gets more noisy and jittery. keep the numbers to less than 10
//you can change up the code to have different options and different shows.

#include <FastLED.h>
#include <Servo.h>

#define LED_PIN 1 // NeoPixel data pin
#define NUM_LEDS 4 // Number of NeoPixel LEDs
#define BRIGHTNESS 200 // Brightness level
#define BUTTON_PIN 2 // Button pin
#define SERVO_PIN 3 // Servo control pin

#define COLOR_ORDER GRB // NeoPixel color order

CRGB leds[NUM_LEDS];
CRGBPalette16 currentPalette;
TBlendType currentBlending;
Servo myServo;

int ledMode = 0; // Keeps track of the current mode
byte prevKeyState = HIGH; // Button is active low

bool case2Active = false; // Tracks if case 2 is active
int sweepCount = 0; // Counts the number of sweeps completed

// Custom Purple Colors Palette
const TProgmemPalette16 PurpleColors_p PROGMEM = {
CRGB::Purple,
CRGB::Purple,
CRGB::MidnightBlue,
CRGB::MidnightBlue,

CRGB::Purple,
CRGB::Purple,
CRGB::BlueViolet,
CRGB::BlueViolet,

CRGB::DarkTurquoise,
CRGB::DarkTurquoise,
CRGB::DarkBlue,
CRGB::DarkBlue,

CRGB::Purple,
CRGB::Purple,
CRGB::BlueViolet,
CRGB::BlueViolet
};

void setup() {
delay(2000); // Power-up safety delay
FastLED.addLeds<WS2812B, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);
currentBlending = LINEARBLEND;

pinMode(BUTTON_PIN, INPUT_PULLUP); // Button setup
myServo.attach(SERVO_PIN); // servo connects to the pin mentioned above
myServo.write(20); // Move servo to initial position (20 degrees)

powerOnLEDEffect(); // Run power-on LED effect
}

void loop() {
byte currKeyState = digitalRead(BUTTON_PIN);

if ((prevKeyState == LOW) && (currKeyState == HIGH)) {
shortKeyPress();
}
prevKeyState = currKeyState;

static uint8_t startIndex = 0;
startIndex = startIndex + 1; /* motion speed */

switch (ledMode) {
case 0:
turnOffLEDs();
break;

case 1:
if (!case2Active) {
case2Active = true; // Activate Case 1
sweepCount = 0; // Reset the sweep counter
turnOffLEDs(); // Turn off LEDs before starting
}
if (case2Active) {
if (sweepCount < 3) {
performServoSweepWithEffect(); // Perform servo sweep with LED effect
} else {
case2Active = false; // Finish Case 1 after 3 sweeps
ledMode++; // Automatically move to the next case
}
}
break;

case 2:
turnOffLEDs();
break;

case 3:
currentPalette = PurpleColors_p; // Custom purple palette
break;

case 4:
currentPalette = RainbowColors_p; // Rainbow colors
break;

case 5:
currentPalette = RainbowStripeColors_p; // Rainbow stripes
break;

case 6:
currentPalette = PartyColors_p; // Party colors
break;
}

if (ledMode != 1 || !case2Active) { // Skip LED updates during servo sweep for Case 1
FillLEDsFromPaletteColors(startIndex);
FastLED.show();
FastLED.delay(1000 / 10); // Adjust for desired speed
}
}

void FillLEDsFromPaletteColors(uint8_t colorIndex) {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = ColorFromPalette(currentPalette, colorIndex, BRIGHTNESS, currentBlending);
colorIndex += 3; // Adjust step size for smoother gradients
}
}

void shortKeyPress() {
ledMode++;
if (ledMode > 6) {
ledMode = 0;
}
}

void performServoSweepWithEffect() {
// 1. Turn off LEDs before the sweep
turnOffLEDs();

// 2. Sweep servo from 20 to 90
for (int pos = 20; pos <= 80; pos += 2) {
myServo.write(pos);
delay(100); // Slower movement
}

// 3. Gradually turn LEDs on to a more purple lotus flower color
for (int brightness = 0; brightness <= BRIGHTNESS; brightness++) {
setColorWithBrightness(CRGB(150, 100, 255), brightness); // Gradual fade-in
delay(10); // Control the speed of fading in
}

FastLED.show();
delay(5000); // Keep lotus flower for 5 seconds

// 4. Sweep servo back from 90 to 20
for (int pos = 80; pos >= 20; pos -= 2) {
myServo.write(pos);
delay(100); // Slower movement
}

// 5. Gradually turn LEDs off
for (int brightness = BRIGHTNESS; brightness >= 0; brightness--) {
setColorWithBrightness(CRGB(150, 100, 255), brightness); // Gradual fade-out
delay(10); // Control the speed of fading out
}

// 6. Turn LEDs off
turnOffLEDs();

sweepCount++; // Increment the sweep counter
}

void setColorWithBrightness(CRGB color, uint8_t brightness) {
CRGB brightColor = color;
brightColor.fadeToBlackBy(255 - brightness); // Apply brightness to color
fill_solid(leds, NUM_LEDS, brightColor); // Fill all LEDs with the adjusted color
FastLED.show();
}

void powerOnLEDEffect() {
setColor(CRGB::Purple); // Turn LEDs purple
FastLED.show();
delay(2000); // Keep LEDs on for 2 seconds
turnOffLEDs(); // Turn LEDs off
}

void setColor(CRGB color) {
fill_solid(leds, NUM_LEDS, color); // Fill all LEDs with the specified color
FastLED.show();
}

void turnOffLEDs() {
fill_solid(leds, NUM_LEDS, CRGB::Black); // Turn off all LEDs
FastLED.show();
}

ARTS & CRAFTS

IMG_0285.jpeg
image (1).png
  1. For the petals, I carefully warmed up the 3D printed "petal bones" using a heat gun to give them a slight natural curve. This helped soften the structure and made the final blooming motion feel more organic.
  2. To finish the surface, I used Korean traditional paper (hanji) and glued it onto both sides of each petal using a glue gun. Hanji was a perfect choice — it absorbs essential oil well for scent diffusion, but also gives the surface a clean, soft, and slightly translucent finish. It balanced the natural feel with a refined visual simplicity.
  3. For the stem, I used a metal rod that added structural support and also doubled as a housing for the electric wires, keeping everything tidy and hidden.
  4. I assembled three separate petal units to mimic the tall, clustered structure of a real lotus flower rather than a single flower. Each unit included a base, petals, and its mechanism. I then attached all three to the wood platform, wired the motors and LEDs together, and finalized the arrangement.
  5. The finished result is something between an object and a sculpture — a cluster of lotus flower-inspired forms that slowly bloom, glow warmly, and release a calming scent into the air.