Universal Domain Panic
Web-based Performance System Built around the Analogy between Web Infrastructure and Attention Economy.
The project is a complex system of performance designed to symbolize the alienating effect of social media by reflecting a series of visual and audio elements on the infrastructure of Internet: UDP (User Datagram Protocol).
“Listen, we are never spontaneous. If there is a big lesson of all those Big Brother and other reality shows, it’s that even when we are just ourselves in private life we always play being ourselves."
Slavoj Žižek on Synthetic Sex and "Being Yourself"
Sources

Individual Project
Summer 2024
#Speculative
#Performance
#Wireless #Web
#Wearable
Enclosure
The enclosure is designed with its form follows function. It vastly maintains a cylindrical shape with necessary cutaways and bump for battery pack. It is secured on the waist with a dovetail joint.
Circuit
I designed a assembly process for all electronics so everything integrates:
- Main circuit with Xiao ESP32C3, 5V Buck Converter, 2.5W  Speaker Amp
- Two SK6812 LED strips
- 4 Ohm Speaker Unit2500 mah LiPo battery
#include <WiFi.h>
#include <WiFiUdp.h>
#include "config.h"
#include <Adafruit_NeoPixel.h>

WiFiUDP udpClient;
const char server[] = "10.23.11.49"; // Server IP address - change this to your Node-RED server IP
const int localPort = 5000;          // Local UDP port for receiving data

// NeoPixel LED strip configuration
#define LED_PIN_1 2  // Left LED strip (WS2812B) - GPIO2
#define LED_PIN_2 10 // Right LED strip (SK6812) - GPIO10
#define LED_COUNT 9  // Number of LEDs per strip

// Configure LED strips for RGBW mode
// NEO_RGBW: 4 bytes per pixel (Red, Green, Blue, White)
// NEO_KHZ800: 800 KHz bitstream
Adafruit_NeoPixel strip1(LED_COUNT, LED_PIN_1, NEO_RGBW + NEO_KHZ800); // WS2812B strip
Adafruit_NeoPixel strip2(LED_COUNT, LED_PIN_2, NEO_RGBW + NEO_KHZ800); // SK6812 strip

// Buzzer pin configuration
#define BUZZER_PIN_POS 3 // Buzzer positive pin (A+)
#define BUZZER_PIN_NEG 4 // Buzzer negative pin (A-)

// Musical note frequencies for major 7th chord
#define NOTE_C4 262 // Root note (C)
#define NOTE_E4 330 // Major third (E)
#define NOTE_G4 392 // Perfect fifth (G)
#define NOTE_B4 494 // Major seventh (B)

void setup()
{
  Serial.begin(115200); // Initialize serial communication for debugging

  // WiFi connection
  Serial.println("Connecting to WiFi...");
  WiFi.mode(WIFI_STA); // Set ESP32 as WiFi station
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  // Wait for WiFi connection
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }

  Serial.println("\nConnected to WiFi!");
  Serial.println(WiFi.localIP()); // Print device's IP address

  // Initialize UDP client
  udpClient.begin(localPort);

  // Initialize LED strips
  strip1.begin();
  strip2.begin();
  strip1.setBrightness(100); // Set brightness for strip1 (0-255)
  strip2.setBrightness(40);  // Set lower brightness for strip2

  // Startup test - flash all LEDs white once
  for (int i = 0; i < LED_COUNT; i++)
  {
    strip1.setPixelColor(i, 0, 0, 0, 255); // RGBW: White only
    strip2.setPixelColor(i, 0, 0, 0, 255);
  }
  strip1.show();
  strip2.show();
  delay(500);

  // Clear all LEDs after test
  strip1.clear();
  strip2.clear();
  strip1.show();
  strip2.show();

  // Initialize buzzer pins
  pinMode(BUZZER_PIN_POS, OUTPUT);
  pinMode(BUZZER_PIN_NEG, OUTPUT);
  digitalWrite(BUZZER_PIN_NEG, LOW); // Set negative pin to ground
}

// Buffer for incoming UDP messages
char messageBuffer[256];

// Generate random pink shade with RGBW values
uint32_t randomPinkShade()
{
  // Base pink values in RGBW order
  uint8_t r = random(180, 256); // High red component
  uint8_t g = 0;                // No green for pure pink
  uint8_t b = r / 2;            // Blue at half of red
  uint8_t w = random(30, 50);   // Slight white component

  // Random effect selection for color variation
  int effect = random(0, 4);
  switch (effect)
  {
  case 0: // Light pink
    r = random(200, 256);
    b = r * 0.7;
    w = 40;
    break;

  case 1: // Deep pink
    r = random(180, 220);
    b = r * 0.6;
    w = 20;
    break;

  case 2: // Purple pink
    r = random(180, 220);
    b = r * 0.9;
    w = 30;
    break;

  case 3: // Coral pink
    r = random(220, 256);
    b = r * 0.4;
    w = 50;
    break;
  }

  b = min(255, (int)b); // Ensure blue value stays within range

  return strip1.Color(r, g, b, w); // Return RGBW color
}

// Set LED strip colors with optional randomization
void setStripsColor(bool randomize = false)
{
  if (randomize)
  {
    // Choose random effect pattern
    int effectType = random(0, 3);

    switch (effectType)
    {
    case 0:
    {                             // Random spot lighting
      int numLeds = random(2, 5); // Light up 2-4 LEDs randomly
      for (int i = 0; i < numLeds; i++)
      {
        int led1 = random(0, LED_COUNT);
        int led2 = random(0, LED_COUNT);
        uint32_t color = randomPinkShade();
        strip1.setPixelColor(led1, color);
        strip2.setPixelColor(led2, color);
      }
    }
    break;

    case 1:
    { // Gradient effect
      uint32_t color = randomPinkShade();
      for (int i = 0; i < LED_COUNT; i++)
      {
        if (random(0, 3) == 0)
        { // 33% chance to change color
          strip1.setPixelColor(i, color);
          strip2.setPixelColor(i, color);
        }
      }
    }
    break;

    case 2:
    { // Alternating pattern
      uint32_t color1 = randomPinkShade();
      uint32_t color2 = randomPinkShade();
      for (int i = 0; i < LED_COUNT; i++)
      {
        if (i % 2 == 0)
        {
          strip1.setPixelColor(i, color1);
          strip2.setPixelColor(i, color2);
        }
        else
        {
          strip1.setPixelColor(i, color2);
          strip2.setPixelColor(i, color1);
        }
      }
    }
    break;
    }
  }
  else
  {
    // Default white lighting with slight color tint
    for (int i = 0; i < LED_COUNT; i++)
    {
      uint8_t r = 20;             // Slight red tint
      uint8_t b = 10;             // Slight blue tint
      uint8_t w = random(60, 80); // Variable white brightness
      strip1.setPixelColor(i, r, 0, b, w);
      strip2.setPixelColor(i, r, 0, b, w);
    }
  }

  // Update both LED strips
  strip1.show();
  strip2.show();
}

// Generate tone with reduced volume using PWM
void diffTone(int freq, int duration)
{
  if (freq == 0)
  {
    digitalWrite(BUZZER_PIN_POS, LOW);
    digitalWrite(BUZZER_PIN_NEG, LOW);
    return;
  }

  unsigned long period = 1000000L / freq; // Period in microseconds
  unsigned long halfPeriod = period / 2;
  unsigned long startTime = millis();

  while (millis() - startTime < duration)
  {
    // Reduced duty cycle for lower volume
    digitalWrite(BUZZER_PIN_POS, HIGH);
    digitalWrite(BUZZER_PIN_NEG, LOW);
    delayMicroseconds(halfPeriod / 4); // 25% duty cycle
    digitalWrite(BUZZER_PIN_POS, LOW);
    digitalWrite(BUZZER_PIN_NEG, LOW);
    delayMicroseconds(halfPeriod * 3 / 4); // 75% off time
  }

  // Ensure buzzer is off after tone
  digitalWrite(BUZZER_PIN_POS, LOW);
  digitalWrite(BUZZER_PIN_NEG, LOW);
}

// Play two random notes from major 7th chord
void playMajor7Chord(int duration)
{
  const int notes[] = {NOTE_C4, NOTE_E4, NOTE_G4, NOTE_B4};
  const int numNotes = 4;

  // Select two different random notes
  int note1Index = random(0, numNotes);
  int note2Index;
  do
  {
    note2Index = random(0, numNotes);
  } while (note2Index == note1Index);

  int singleNoteDuration = duration / 2;

  // Play sequence of two notes
  diffTone(notes[note1Index], singleNoteDuration);
  delay(10); // Brief pause between notes
  diffTone(notes[note2Index], singleNoteDuration);
}

void loop()
{
  // Check for incoming UDP packets
  int packetSize = udpClient.parsePacket();
  if (packetSize)
  {
    // Read UDP data
    udpClient.read(messageBuffer, 255);
    messageBuffer[packetSize] = '\0';

    Serial.print("Received data: ");
    Serial.println(messageBuffer);

    // Parse duration from message
    int duration = atoi(messageBuffer);

    if (duration > 0)
    {
      // Send acknowledgment
      udpClient.beginPacket(udpClient.remoteIP(), udpClient.remotePort());
      udpClient.print("OK");
      udpClient.endPacket();

      unsigned long startTime = millis();
      setStripsColor(false); // Initial white state

      // Main effect loop
      while (millis() - startTime < duration)
      {
        setStripsColor(true); // Random color patterns
        playMajor7Chord(600); // Play two random notes
        delay(200);           // Pause between iterations
      }

      // Clean up - turn everything off
      digitalWrite(BUZZER_PIN_POS, LOW);
      digitalWrite(BUZZER_PIN_NEG, LOW);
      strip1.clear();
      strip2.clear();
      strip1.show();
      strip2.show();
    }
  }

  // WiFi connection monitoring and auto-reconnect
  static unsigned long lastWiFiCheck = 0;
  if (millis() - lastWiFiCheck > 30000)
  { // Check every 30 seconds
    lastWiFiCheck = millis();
    if (WiFi.status() != WL_CONNECTED)
    {
      Serial.println("WiFi disconnected, attempting reconnection...");
      WiFi.begin(WIFI_SSID, WIFI_PASS);
    }
  }
}
var devicePairs = [
  {
    Waist: { ip: "", port: 5000},
    TD: { ip: "", port: 5000}
  },
  {
    Waist: { ip: "", port: 5000},
    TD: { ip: "", port: 5001 }
  },
  {
    Waist: { ip: "", port: 5000},
    TD: { ip: "", port: 5001 }
  },
];


// Initialize the interval and counter if not already set
if (typeof flow.get('initialized') === 'undefined') {
  flow.set('initialized', true);
  flow.set('delay', 5000); // Starting delay (5 seconds)
  flow.set('counter', 0);
}

// Select a random device pair
var randomIndex = Math.floor(Math.random() * devicePairs.length);
var selectedPair = devicePairs[randomIndex];

// Create messages for both TD and Waist
var tdMsg = {
  ip: selectedPair.TD.ip,
  port: selectedPair.TD.port,
};

var waistMsg = {
  ip: selectedPair.Waist.ip,
  port: selectedPair.Waist.port,
};

// Retrieve and update the delay from the flow context
var delay = flow.get('delay') || 10000;
delay = delay > 100 ? delay - 100 : 100; // Decrease by 100ms, but not less than 100ms
flow.set('delay', delay);

var counter = flow.get('counter') || 0;
counter += 1;
flow.set('counter', counter);

// Add payload and delay to messages
tdMsg.payload = counter;
tdMsg.delay = delay;

waistMsg.payload = counter;
waistMsg.delay = delay;

// Return an array of messages to send both
return [tdMsg, waistMsg];