import processing.sound.*; import processing.serial.*; PImage[] lotusFrames; PImage lotus; PImage flower; int totalFrames = 38; int currentIndex = 0; int gestureBoost = 0; boolean sprinkleElements = false; boolean showFallAnimation = false; ArrayList fallingLotuses = new ArrayList<>(); float animProgress = 0; // Sound SoundFile ambientSound; float soundVolume = 0; float maxVolume = 0.6; boolean startedSound = false; // Rotation + pulsing for final frame float rotationAngle = 0; float pulseScale = 1.0; // Wave background parameters float waveOffset = 0; // Floating flowers ArrayList floatingFlowers = new ArrayList<>(); int flowerCount = 12; boolean triggerLotusAnimation = false; boolean animationComplete = false; // ---- Conch Animation variables ---- boolean triggerConchAnimation; PImage[] conchFrames; int totalConchFrames = 32; int currentConchIndex = 0; int conchFrameCounter = 0; int conchFrameDuration = 4; float conchRotationAngle = 0; PImage smoke1, smoke2; PImage chalkBackground; float ambientSoundVolume = 0; float ambientMaxVolume = 0.6; boolean ambientSoundStarted = false; ArrayList clouds = new ArrayList<>(); int cloudCount = 20; // ---- Po Kolam variables ---- PImage[] kolamFrames; int totalKolamFrames = 28; int currentKolamFrame = 0; int kolamFrameDelay = 10; int kolamFrameCounter = 0; float kolamRotationAngle = 0; SmokeCloud[] kolamSmokes; boolean showConchAnimation = false; boolean showPoKolamAnimation = false; // --- Serial and RFID variables --- Serial myPort; String inputString = ""; String detectedRFID = ""; void setup() { size(1920, 1200); kolamSmokes = new SmokeCloud[10]; // or however many you want for (int i = 0; i < kolamSmokes.length; i++) { kolamSmokes[i] = new SmokeCloud(); } conchFrames = new PImage[totalConchFrames]; for (int i = 0; i < totalConchFrames; i++) { conchFrames[i] = loadImage("Conch " + (i + 1) + ".png"); } smoke1 = loadImage("smoke1.png"); smoke2 = loadImage("smoke2.png"); chalkBackground = loadImage("chalkboard.png"); // if applicable for (int i = 0; i < cloudCount; i++) { clouds.add(new SmokeCloud()); } // Setup serial println("Available serial ports:"); for (int i = 0; i < Serial.list().length; i++) { println(i + ": " + Serial.list()[i]); } // Replace [0] with your port index if needed String portName = "/dev/tty.usbmodem1101"; myPort = new Serial(this, portName, 9600); myPort.bufferUntil('\n'); println("Connected to: " + portName); noCursor(); // Load images lotusFrames = new PImage[totalFrames]; for (int i = 0; i < totalFrames; i++) { String filename = "Lotus " + (i + 1) + ".png"; lotusFrames[i] = loadImage(filename); } lotus = loadImage("Lotus.png"); flower = loadImage("flower.png"); imageMode(CENTER); frameRate(60); for (int i = 0; i < flowerCount; i++) { floatingFlowers.add(new FloatingFlower(random(width), random(height), random(TWO_PI), random(0.2f, 0.6f))); } ambientSound = new SoundFile(this, "meditation.mp3"); ambientSound.amp(0); ambientSound.loop(); } void draw() { drawWaveBackground(); // Read serial input continuously readSerialInput(); if (triggerLotusAnimation) { if (!startedSound && currentIndex == 0 && animProgress > 0) { startedSound = true; } if (startedSound && soundVolume < maxVolume) { soundVolume += 0.0005; ambientSound.amp(constrain(soundVolume, 0, maxVolume)); } float opacityProgress = 0; if (currentIndex >= 5 && currentIndex <= 15) { opacityProgress = map(currentIndex, 5, 15, 0, 255); } else if (currentIndex > 15) { opacityProgress = 255; } for (FloatingFlower ff : floatingFlowers) { ff.update(); ff.display(opacityProgress); } if (!showFallAnimation) { for (int i = 0; i < currentIndex; i++) { PImage img = lotusFrames[i]; image(img, width / 2, height / 2-100, img.width * 2.0, img.height * 2.0); } if (currentIndex < totalFrames) { PImage img = lotusFrames[currentIndex]; float fadeDuration = getFadeDurationForFrame(currentIndex + 1); float alpha = map(animProgress, 0, fadeDuration, 0, 255); alpha = constrain(alpha, 0, 255); if (alpha > 10) { pushMatrix(); translate(width / 2, height / 2-100); tint(255, alpha); image(img, 0, 0, img.width * 2.0, img.height * 2.0); popMatrix(); noTint(); } animProgress++; if (animProgress > fadeDuration) { animProgress = 0; currentIndex++; } } else { showFallAnimation = true; spawnFallingLotuses(12); } } else { PImage lastLotus = lotusFrames[totalFrames - 1]; float pulse = sin(frameCount * 0.02) * 0.05; pulseScale = 2.0 + pulse; pushMatrix(); translate(width / 2, height / 2-60); rotate(rotationAngle); image(lastLotus, 0, 0, lastLotus.width * pulseScale, lastLotus.height * pulseScale); popMatrix(); rotationAngle += radians(0.35); for (FallingLotus fl : fallingLotuses) { fl.update(); fl.display(); } if (frameCount % 600 == 0) { animationComplete = true; } } } else if (triggerConchAnimation) { runConchAnimation(); // Just call the function here } else { // default floating flowers (or other idle animation) for (FloatingFlower ff : floatingFlowers) { ff.update(); ff.display(100); } } waveOffset += 0.5; } // --- SmokeCloud class FIXED --- class SmokeCloud { float x, y; float scale; float alpha; float fadeSpeed; float maxAlpha; int state; // 0 = fade in, 1 = fade out PImage cloudImg; int delay; SmokeCloud() { reset(true); } void reset(boolean firstTime) { x = random(width); y = random(height); scale = random(0.3, 0.6); fadeSpeed = random(0.3, 0.6); maxAlpha = random(20, 60); cloudImg = random(1) < 0.5 ? smoke1 : smoke2; alpha = 0; state = 0; delay = int(random(60, 300)); } void update() { if (delay > 0) { delay--; return; } if (state == 0) { alpha += fadeSpeed; if (alpha >= maxAlpha) state = 1; } else { alpha -= fadeSpeed; if (alpha <= 0) reset(false); } } void display() { tint(255, constrain(alpha, 0, 255)); image(cloudImg, x, y, cloudImg.width * scale, cloudImg.height * scale); noTint(); } } void runConchAnimation() { if (!ambientSoundStarted) { ambientSound.amp(ambientMaxVolume); ambientSoundStarted = true; } background(0); // or image(chalkBackground, 0, 0); if you're using a background // Draw conch frames if (conchFrameCounter++ >= conchFrameDuration) { currentConchIndex = (currentConchIndex + 1) % totalConchFrames; conchFrameCounter = 0; } pushMatrix(); translate(width/2, height/2-120); rotate(radians(conchRotationAngle)); if (conchFrames != null && conchFrames[currentConchIndex] != null) { image(conchFrames[currentConchIndex], 0, 0); } popMatrix(); conchRotationAngle += 0.1; for (SmokeCloud cloud : clouds) { cloud.update(); cloud.display(); } } void readSerialInput() { while (myPort.available() > 0) { char inChar = (char) myPort.read(); if (inChar == '\n') { inputString = inputString.trim(); if (inputString.startsWith("GESTURE:")) { handleGesture(inputString); } else if (inputString.length() > 0) { handleRFID(inputString); } inputString = ""; } else { inputString += inChar; } } } void handleRFID(String tagID) { println("RFID Tag detected: " + tagID); triggerLotusAnimation = false; triggerConchAnimation = false; showPoKolamAnimation = false; if (tagID.equalsIgnoreCase("8B C7 15 05")) { triggerLotusStart(); } else if (tagID.equalsIgnoreCase("89 34 81 2F")) { triggerConchAnimation = true; currentConchIndex = 0; conchFrameCounter = 0; conchRotationAngle = 0; ambientSoundStarted = false; } else if (tagID.equalsIgnoreCase("DA 78 75 2F")) { showPoKolamAnimation = true; } else { println("Unsupported tag detected."); } } void handleGesture(String g) { // -------- LOTUS -------- if (triggerLotusAnimation) { if (g.equals("GESTURE:CLOCKWISE")) { currentIndex = min(currentIndex + 3, totalFrames - 1); } if (g.equals("GESTURE:UP")) { spawnFallingLotuses(6); } } // -------- CONCH -------- if (triggerConchAnimation) { if (g.equals("GESTURE:CLOCKWISE")) { conchFrameDuration = max(1, conchFrameDuration - 1); } if (g.equals("GESTURE:UP")) { for (int i = 0; i < 4; i++) { clouds.add(new SmokeCloud()); } } } // -------- PO KOLAM -------- if (showPoKolamAnimation) { if (g.equals("GESTURE:CLOCKWISE")) { currentKolamFrame = min(currentKolamFrame + 4, totalKolamFrames - 1); } if (g.equals("GESTURE:UP")) { sprinkleElements = true; } } } void triggerLotusStart() { triggerLotusAnimation = true; currentIndex = 0; animProgress = 0; showFallAnimation = false; startedSound = false; soundVolume = 0; ambientSound.amp(0); animationComplete = false; } float getFadeDurationForFrame(int frameNum) { return 45; } void spawnFallingLotuses(int count) { for (int i = 0; i < count; i++) { float x; if (random(1) < 0.5) { x = random(100, 400); } else { x = random(width - 400, width - 100); } float y = random(-height, 0); fallingLotuses.add(new FallingLotus(x, y)); } } class FallingLotus { float x, y; float speed; float alpha; float scale; FallingLotus(float x, float y) { this.x = x; this.y = y; this.speed = random(2.0, 4.0); this.alpha = 0; this.scale = random(0.8, 1.5); } void update() { y += speed; if (alpha < 255) alpha += 1; } void display() { tint(255, alpha); image(lotus, x, y, lotus.width * scale, lotus.height * scale); noTint(); } } class FloatingFlower { float x, y; float angle; float rotationSpeed; float driftSpeedX, driftSpeedY; FloatingFlower(float x, float y, float angle, float rotationSpeed) { this.x = x; this.y = y; this.angle = angle; this.rotationSpeed = rotationSpeed; this.driftSpeedX = random(-0.3, 0.3); this.driftSpeedY = random(-0.1, 0.1); } void update() { x += driftSpeedX; y += driftSpeedY; angle += rotationSpeed; if (x < -flower.width) x = width + flower.width; else if (x > width + flower.width) x = -flower.width; if (y < -flower.height) y = height + flower.height; else if (y > height + flower.height) y = -flower.height; } void display(float alpha) { pushMatrix(); translate(x, y); rotate(angle); tint(255, alpha); image(flower, 0, 0, flower.width * 0.7, flower.height * 0.7); noTint(); popMatrix(); } } void drawWaveBackground() { background(220, 240, 255); noFill(); stroke(100, 150, 200, 50); strokeWeight(1); float spacing = 20; float time = frameCount * 0.07; for (float y = 0; y <= height; y += spacing) { beginShape(); for (float x = 0; x <= width; x += 2) { float dx = x - width / 2; float dy = y - height / 2; float dist = sqrt(dx * dx + dy * dy); float wave = sin(dist * 0.1 - time) * 10; vertex(x, y + wave); } endShape(); } for (float x = 0; x <= width; x += spacing) { beginShape(); for (float y = 0; y <= height; y += 2) { float dx = x - width / 2; float dy = y - height / 2; float dist = sqrt(dx * dx + dy * dy); float wave = sin(dist * 0.1 - time) * 10; vertex(x + wave, y); } endShape(); } }