Concept: Interactive Musical Garden is an innovative interactive art installation that marries technology with natural aesthetics. It incorporates ultrasonic sensors embedded with 3D-printed transparent roses, allowing each rose to respond to user interaction by lighting up, playing music, and spawning a digital flower on a p5.js canvas. This project aims to create a communal yet personalized musical and visual experience where each interaction contributes to a growing digital garden.
Arduino Code Overview: The Arduino code controls the ultrasonic sensors and LEDs. It reads the distance measurements from the sensors and turns on an LED if an object (e.g., a user’s hand) is detected within a specified range. It also sends a signal to the p5.js application via serial communication when a flower should be spawned.
#include <Arduino.h> // Define pins for the ultrasonic sensors and LEDs #define NUM_SENSORS 5 int trigPins[NUM_SENSORS] = {2, 3, 4, 5, 6}; int echoPins[NUM_SENSORS] = {7, 8, 9, 10, 11}; int ledPins[NUM_SENSORS] = {12, 13, A0, A1, A2}; // Function to measure distance long readDistance(int triggerPin, int echoPin) { digitalWrite(triggerPin, LOW); delayMicroseconds(2); digitalWrite(triggerPin, HIGH); delayMicroseconds(10); digitalWrite(triggerPin, LOW); long duration = pulseIn(echoPin, HIGH); return duration * 0.034 / 2; // Convert to distance in cm } void setup() { Serial.begin(9600); for (int i = 0; i < NUM_SENSORS; i++) { pinMode(trigPins[i], OUTPUT); pinMode(echoPins[i], INPUT); pinMode(ledPins[i], OUTPUT); } } void loop() { for (int i = 0; i < NUM_SENSORS; i++) { long distance = readDistance(trigPins[i], echoPins[i]); if (distance < 20) { digitalWrite(ledPins[i], HIGH); Serial.print("Bloom "); Serial.println(i + 1); // Send sensor number to p5.js } else { digitalWrite(ledPins[i], LOW); } } delay(100); // Debouncing }
p5.js Code Overview: The p5.js application runs in a web browser and uses the serial communication data to create flowers on the screen each time a sensor is triggered. It also manages the playback of sound for each interaction.
// Define the Flower class for visual representation class Flower { constructor(x, y) { this.x = x; this.y = y; this.size = 5; this.growthRate = random(0.05, 0.2); this.fullSize = random(30, 70); this.petals = floor(random(4, 9)); this.petalSize = this.fullSize / 2; this.color = [random(100, 255), random(100, 255), random(100, 255)]; } grow() { if (this.size < this.fullSize) { this.size += this.growthRate; } } show() { push(); translate(this.x, this.y); noStroke(); fill(this.color[0], this.color[1], this.color[2]); for (let i = 0; i < this.petals; i++) { rotate(TWO_PI / this.petals); ellipse(0, this.size / 4, this.petalSize, this.size); } fill(255, 204, 0); ellipse(0, 0, this.size / 4, this.size / 4); pop(); } } let flowers = []; let serial; let flowerSound; function preload() { flowerSound = loadSound('bells.wav'); } function setup() { let canvas = createCanvas(windowWidth, windowHeight); canvas.style('display', 'block'); background(0); serial = new p5.SerialPort(); serial.open('/dev/tty.usbmodem1101'); serial.on('data', serialEvent); } function draw() { background(0); flowers.forEach(flower => { flower.grow(); flower.show(); }); } function serialEvent() { let data = serial.readStringUntil('\n').trim(); if (data.startsWith("Bloom")) { let parts = data.split(" "); if (parts.length === 2) { let index = parseInt(parts[1]) - 1; if (!isNaN(index) && index >= 0 && index < 5) { createFlower(); } } } } function createFlower() { let x = random(width); let y = random(height); let flower = new Flower(x, y); flowers.push(flower); playSound(); } function playSound() { if (flowerSound.isPlaying()) { flowerSound.stop(); } flowerSound.play(); } function keyPressed() { if (key === 'f' || key === 'F') { let fs = fullscreen(); fullscreen(!fs); } } function windowResized() { resizeCanvas(windowWidth, windowHeight); }
How the Code Works:
Serial Communication: p5.js uses the p5.serialport library to establish a serial connection with the Arduino. This connection allows it to receive data (like sensor triggers) from the Arduino.
Flower Generation: When a “Bloom” command is received via serial (indicating that a sensor was triggered), p5.js generates a digital flower at a random location on the canvas.
Sound Playback: Simultaneously with the flower generation, a sound file is played to provide auditory feedback, making the experience more immersive.
Planning the Interaction Flow:
Detection: A user places their hand over one of the 3D-printed roses.
Sensor Activation: The corresponding ultrasonic sensor detects the presence based on the distance and triggers a response.
LED Feedback: The LED beneath the detected rose lights up, providing immediate visual feedback.
Visual and Auditory Display: The user sees a new flower appearing on the screen and hears a sound, linking their physical interaction with a digital outcome.
Acknowledgements: Special thanks to Stefania for helping me with the idea and the implementation and to my fiancé for helping me setup a beautiful garden using a pizza box 🙂