Skip to main content

Lunar Fusion

a large seashell with electronic lights shining from within on a shelf with a toy sailing ship

You Can Hear the Ocean

Interactive Audio Sculpture

Remember that sense of wonder you experienced the first time someone told you to hold a shell to your hear to hear the ocean? As we grow older, it can become more difficult to find that sense of wonder. I create magical objects to help reignite that sense of wonder. I use electronics to remind myself that technology is magic, and that I can create magic if I learn to use the right tools.

When this shell is turned over, random sounds of the sea are activated along with oceanic lights. Dolphins, waves crashing, sailors singing - turn it over and listen for the magic.

Project Details

Tools

  • Electronics
  • Arduino Nano
  • Natural Seashell
  • Magnets

Song Credits

  • Great Open Sea - The Wellington Sea Shanty Society
  • Drunken Sailor - Bretherin Coast
  • The Worst Old Ship - Bretherin Coast
  • Storm Along - Bretherin Coast

The Making Process

wiring for an Arduino project

I use proto boards and jump wires to test the code and wiring. Seen in this image, I am also using tape and velcro to keep things in place while I test.

Step 1:Code and Wiring

This project required quite a few components:

  • Arduino Nano
  • Accelerometer
  • Speaker
  • Amp
  • SD data storage
  • Two power sources

I needed to build a script that would detect the accelerometer being flipped over which would trigger oceanic lighting effects shining from inside the shell, pull a random sound file from an SD card, and play it on a tiny speaker. Everything had to fit into the shell. The whole thing required two power sources because without this, the power being drawn by the light interfered with the sound coming through the speaker.

Project Arduino Code

  #include "FastLED.h"
  #define NUM_LEDS 7
  CRGB leds[NUM_LEDS];
  #define LED_PIN 6
  uint8_t gBrightness = 255;
   
  #include "SD.h"
  #define SD_ChipSelectPin 4
  #include "SPI.h"
  #include "TMRpcm.h"
  TMRpcm audio;
  File root;
  char toPlay[20];
  int indexToWrite = 0;
   
  #define SPEAKER_PIN 9
  #define DISABLE_SPEAKER2
   
  // ACCELEROMETER
  int Zval;
  int accPin = A1;
   
  void setup() {
    audio.speakerPin = 9;
    FastLED.addLeds(leds, NUM_LEDS)
        .setCorrection(TypicalLEDStrip);
    Serial.begin(9600);
    pinMode(accPin, INPUT);
    pinMode(SPEAKER_PIN, OUTPUT);
    digitalWrite(SPEAKER_PIN, LOW);
    pinMode(LED_PIN, OUTPUT);
    Zval = analogRead(accPin);
   
    if (!SD.begin(SD_ChipSelectPin)) {
      Serial.println("SD fail");
      return;
    }
    Serial.println("OK!");
    root = SD.open("/");  // open SD card main root
    audio.setVolume(0);
  }
   
  void loop() {
    Zval = analogRead(accPin);
    Serial.print(analogRead(accPin));
    Serial.println();
   
    // only the z-axis is needed to detect it's been flipped over
    if (Zval > 460) {
      digitalWrite(SPEAKER_PIN, LOW);
      audio.pause();
      audio.setVolume(0);
      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED[1].showLeds(gBrightness);
    }
   
    else if (Zval < 460) {
      // SOUNDS
      digitalWrite(SPEAKER_PIN, HIGH);
      randomSeed(analogRead(0));
      int randNumber = random(20);  // random number between 0 and 18
      makeName(randNumber, 0);      // generate file name
      audio.play(toPlay);
      audio.quality(10);
      audio.setVolume(4);  // Volume below 5 causes lights to whine
   
      while (audio.isPlaying()) {
        delay(500);
        // LIGHTS
        TwinkleRandom(5, 200, false);
      }
    };
  }
   
  void TwinkleRandom(int Count, int SpeedDelay, boolean OnlyOne) {
    setAll(0, 0, 0);
   
    for (int i = 0; i < Count; i++) {
      setPixel(random(NUM_LEDS), random(0, 55), random(0, 0), random(0, 255));
      showStrip();
      delay(SpeedDelay);
      if (OnlyOne) {
        setAll(0, 0, 0);
      }
    }
   
    delay(SpeedDelay);
  }
   
  // ******* sd card and sound ******* //
  void makeName(int number, int depth) {  // generates a file name 0.WAV to
                                          // 9999.WAV suppressing leading zeros
    if (number > 9) {
      makeName(number / 10, ++depth);  // recursion
      depth--;
      number =
          number %
          10;  // only have to deal with the next significant digit of the number
    }
    toPlay[indexToWrite] = (number & 0xf) | 0x30;
    indexToWrite++;
    if (depth > 0)
      return;  // return if we have more levels of recursion to go
    else {     // finish off the string with the wave extesion
      toPlay[indexToWrite] = '.';
      toPlay[1 + indexToWrite] = 'W';
      toPlay[2 + indexToWrite] = 'A';
      toPlay[3 + indexToWrite] = 'V';
      toPlay[4 + indexToWrite] = '\0';  // terminator
      indexToWrite = 0;                 // reset pointer for next time we enter
    }
  }
   
  // LIGHTS LIGHTS LIGHTS *************************************
   
  void Twinkle(byte red, byte green, byte blue, int Count, int SpeedDelay,
               boolean OnlyOne) {
    setAll(0, 0, 0);
   
    for (int i = 0; i < Count; i++) {
      setPixel(random(NUM_LEDS), red, green, blue);
      showStrip();
      delay(SpeedDelay);
      if (OnlyOne) {
        setAll(0, 0, 0);
      }
    }
   
    delay(SpeedDelay);
  }
   
  // *** REPLACE TO HERE ***
   
  void showStrip() {
  #ifdef ADAFRUIT_NEOPIXEL_H
    // NeoPixel
    strip.show();
  #endif
  #ifndef ADAFRUIT_NEOPIXEL_H
    // FastLED
    FastLED.show();
  #endif
  }
   
  void setPixel(int Pixel, byte red, byte green, byte blue) {
  #ifdef ADAFRUIT_NEOPIXEL_H
    // NeoPixel
    strip.setPixelColor(Pixel, strip.Color(red, green, blue));
  #endif
  #ifndef ADAFRUIT_NEOPIXEL_H
    // FastLED
    leds[Pixel].r = red;
    leds[Pixel].g = green;
    leds[Pixel].b = blue;
  #endif
  }
   
  void setAll(byte red, byte green, byte blue) {
    for (int i = 0; i < NUM_LEDS; i++) {
      setPixel(i, red, green, blue);
    }
    showStrip();
  }
   
Monet soldering electronics

I solder all the wires to the components to stabilize the connections. This is especially important for interactive art, which can be jostled quite a lot.

Step 2:Soldering

Soldering keeps the wires secure and gives me a chance to start trimming them shorter so that all the components can fit inside the shell.

electronic components overflowing and being tucked into a seashell

Fitting all the components into this seashell was a challenge. I placed the speaker deepest inside to take advantage of the shell's acoustics.

Step 3:Assemblage

I made sure the electronics fit inside this beautiful natural conch shell. I placed velcro inside the shell to hold the accelerometer in the correct position. I glued small magnets around the inside and placed magnets inside of the fabric covering. The resulting cover holds everything inside of the shell and diffuses the lighting.

The Finished Seashell

finished interactive seashell

I covered the electronics with layers of fabric that allow the light inside to show. I secured the fabric with magnets.

finished interactive seashell

The finished product! The fabric diffuses the LEDs beautifully and the speaker echos into the shell's chamber, creating a wonderful otherworldly effect.

The Magic Seashell in Action

Any sufficiently advanced technology is indistinguishable from magic.
- Arthur C. Clarke