Skip to content

Experience 1

submerged in a river

graph



moodboard


look1

“isolate”;“transcending”

  • Bamboo jersey dress w felt details

Designed to hug the body, with no armholes to evoke the underwater isolating sensation, and a human cocoon. With a drooping silhouette, resembling the appearance of wetness. The felted details represent the river moss, growing on the body.

“prickly”;”(…)crystal spikes piercing your skin.(…)”

  • 3D-printed wearable sculpture

Structure of prickly spines, extending toward your hand, like the cold water piercing the skin. With water droplets spreading across the surface.

“(…)and you wonder how cold the water will be.(…)”;“rejuvenating”;”transcending”

  • Striped bio-plastic scarf

Representing a glossy, light-reflective water texture. With air bubbles forming on the surface, that form as you breathe underwater.

“(…)you feel the sharpness of crystal spikes piercing your skin. It hurts but it also makes you feel alive.(…)”

  • Crystallized tights

Representing crystallised skin from the cold water that freezes into solid ice. Starting from the feet, spreading across the legs.

skecthes


bamboo jersey dress

This dress is designed to ensure that the stretchy knit fabric hugs the body closely while maintaining flexibility for movement and comfort. It draws inspiration from 1920s dropped-waist dresses with a modern interpretation.

The dress is adorned with needle-felted abstract flower shapes in shades of green wool, reminiscent of river moss.


For the prototype, I began by draping the fabric on a mannequin to directly develop the foundational ideas for the pattern–no armholes, a draped neckline, two front pockets with darts, a dropped waistline, a short skirt, and a strap for fastening the dress.

prototype1


pattern

After completing the first prototype, I revised the pattern to explore how it could be simplified into a single-piece cutting. This involved merging all the individual pattern pieces and identifying key points for stitching to create a functional, wearable dress while minimizing seams and enhancing efficiency.


dress


look1


needle felting


felt



electronics

  • Initial idea was to incorporate conductivity into the felted details of the dress and add an interior conductive garment. This garment, when rubbed against the felted details, would trigger sounds. It could work through friction, act like a type of Velcro material, or even respond to pressure or stretch.

  • Given the timelines and the scope of work I want to achieve with this look, I decided to focus on a more streamlined approach. The idea is to use a conductive rubber cord stretch sensor, placed along the shoulder line at the back of the dress or in between the elbows. This sensor would be activated when the wearer moves, stretching the cord and triggering sounds.

The sounds will be samples created by the artist unpsii, designed to evoke underwater movement or vocals or noise. For sound output, I plan to use a DFPlayer Mini, a small, low-cost MP3 module with a simplified output directly to a active speaker, JBL go 2.

Additionally, the system will use wireless communication—a sender and receiver setup—between the stretch sensor input and the sound output. This would allow the speaker and receiver circuit to be discreetly hidden within the wearable sculpture and the sender circuit hidden below the dress.


wireless


stretch sensor input


input

Building this Circuit on a Breadboard:

  • Connect one end of the stretch sensor to 3.3V and the other to A0.

  • Attach a 10kΩ pull-down resistor between A0 and GND.


INPUT (SENDER):

#include <esp_now.h>
#include <WiFi.h>

#define STRETCH_SENSOR_PIN A0  // Analog pin for stretch sensor

const int minSensorValue = 100;  // Value when sensor is NOT stretched
const int maxSensorValue = 2800;  // Value when sensor is fully stretched
const int minVolume = 0;   // Minimum volume
const int maxVolume = 30;  // Maximum volume

// Replace with receiver MAC address
uint8_t broadcastAddress[] = { 0x64, 0xe8, 0x33, 0x51, 0x0f, 0x8c };

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
int volume;
} struct_message;

// Create a struct_message called myData
struct_message myData;

esp_now_peer_info_t peerInfo;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.print("\r\nLast Packet Send Status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void setup() {
// Init Serial Monitor
Serial.begin(115200);
pinMode(STRETCH_SENSOR_PIN, INPUT);  // Set sensor pin as input
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);

// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
}

// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Transmitted packet
esp_now_register_send_cb(OnDataSent);

// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;

// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
}
}

void loop() {
int sensorValue = analogRead(STRETCH_SENSOR_PIN);  // Read stretch sensor

// Apply a moving average filter to smooth sensor readings
static float smoothSensorValue = sensorValue;
smoothSensorValue = (smoothSensorValue * 0.8) + (sensorValue * 0.2);  // 80% old value, 20% new

// Map the smoothed sensor value to a smooth volume range (0 to 30)
int volume = map(smoothSensorValue, minSensorValue, maxSensorValue, minVolume, maxVolume);
volume = constrain(volume, minVolume, maxVolume);  // Ensure volume stays within range

Serial.print("Sensor Value: ");
Serial.print(sensorValue);
Serial.print(" | Smoothed: ");
Serial.print(smoothSensorValue);
Serial.print(" | Volume: ");
Serial.println(volume);

myData.volume = volume;

// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&myData, sizeof(myData));

if (result == ESP_OK) {
    Serial.println("Sent with success");
} else {
    Serial.println("Error sending the data");
}

delay(200);
}



STRETCH SENSOR TEST w LED



CODE:

#define STRETCH_SENSOR_PIN A0  // Use GPIO1 (A0)
#define LED_PIN 5  // Change this to any PWM-capable GPIO pin

void setup() {
Serial.begin(115200);  // Start serial monitor
pinMode(LED_PIN, OUTPUT);  // Set LED pin as output
analogWrite(LED_PIN, 0);  // Ensure LED starts off
}

void loop() {
int sensorValue = analogRead(STRETCH_SENSOR_PIN);  // Read stretch sensor value

// Map the sensor value from 2600-3400 to 0-100 range
int mappedValue = map(sensorValue, 2600, 3400, 0, 100);

// Map the same value to LED brightness (0-255 for PWM control)
int ledBrightness = map(mappedValue, 0, 100, 0, 255);

// Print the mapped value
Serial.print("Mapped Value: ");
Serial.println(mappedValue);

// Print the LED brightness range
Serial.print("LED Brightness: ");
Serial.println(ledBrightness);

// Determine stretch levels and LED brightness
if (mappedValue < 25) {  
Serial.println("Stretch Level 1: High Stretch - LED Max Brightness");  
ledBrightness = 255;  // Max brightness
} 
else if (mappedValue >= 25 && mappedValue < 45) {  
Serial.println("Stretch Level 2: Medium Stretch - LED Medium Brightness");  
ledBrightness = 128;  // Medium brightness
} 
else if (mappedValue >= 45 && mappedValue < 80) {  
Serial.println("Stretch Level 3: Light Stretch - LED Low Brightness");  
ledBrightness = 50;  // Low brightness
} 
else {  
Serial.println("No Stretch - LED Off");  
ledBrightness = 0;  // Turn off LED when no stretch is detected
}

// Apply brightness to LED using PWM
analogWrite(LED_PIN, ledBrightness);

delay(500);  // Wait before next reading
}


output


sound output


output

Building this Circuit on a Breadboard:

  • Connect ESP32's TX to DFPlayer Mini's RX through a 1kΩ resistor.

  • Connect ESP32's RX directly to DFPlayer Mini's TX.

  • Connect DFPlayer Mini’s VCC to ESP32's 5V and GND to GND.

  • Insert a microSD card with the sound files into the DFPlayer Mini.

  • Add a 1000µF capacitor between DFPlayer Mini's VCC (+) and GND (-) to help stabilize power supply fluctuations.

  • Connect a passive speaker to the DFPlayer Mini’s SPK1 and SPK2 pins (); active speaker to DAC_R or DAC_I and GND through an audio jack.

  • Connect active speaker via an audio jack as follows:

Connect the positive terminal of the audio jack to the positive terminal of the 10µF capacitor, then route it through two 100Ω resistors in series before connecting to the DAC_C pin of the DFPlayer Mini.

Connect the negative terminal of the audio jack through the negative terminal 10µF capacitor to the GND of the DFPlayer Mini.

  • Attach a Bluetooth antenna to the ESP32.


OUTPUT (RECEIVER) CODE:

#include <esp_now.h>
#include <WiFi.h>
#include "DFRobotDFPlayerMini.h"

#define FPSerial Serial1

DFRobotDFPlayerMini myDFPlayer;
int volume;
int lastVolume = -1;  // Store last volume level to detect changes

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
int volume;  
} struct_message;

// Create a struct_message called myData
struct_message myData;

// callback function that will be executed when data is received
void OnDataRecv(const esp_now_recv_info_t *recvInfo, const uint8_t *incomingData, int len) {
memcpy(&myData, incomingData, sizeof(myData));

volume = myData.volume;
Serial.print("Received Volume: ");
Serial.println(volume);

if (volume != lastVolume) {  // Only update if volume changes
    lastVolume = volume;
    if (volume == 0) {
    myDFPlayer.pause();  // Pause when volume is 0
    } else {
    myDFPlayer.volume(volume);  // Adjust volume
    myDFPlayer.start();  // Resume playback
    }
}
}

void setup() {
Serial.begin(115200);

WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
}
// Once ESPNow is successfully Init, we will register for recv CB to get recv packer info
esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));


FPSerial.begin(9600, SERIAL_8N1, /*rx =*/44, /*tx =*/43);

Serial.println(F("DFRobot DFPlayer Mini - Smooth Volume Control"));

if (!myDFPlayer.begin(FPSerial, /*isACK = */ true, /*doReset = */ true)) {
    Serial.println(F("Unable to begin DFPlayer! Check connections and SD card."));
    return;
}

Serial.println(F("DFPlayer Mini online."));

myDFPlayer.setTimeOut(2500);
myDFPlayer.volume(0);  // Start at volume 0 (silent)
myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
myDFPlayer.outputDevice(DFPLAYER_DEVICE_AUX);

Serial.println("Playing track 1...");
myDFPlayer.play(1);  // Start playing track 1

}

void loop() {
// Nothing needed here, volume is updated in OnDataRecv()
delay(200);  // Prevents excessive CPU usage
}



VOLUME CONTROL

#include "Arduino.h"
#include "DFRobotDFPlayerMini.h"

#define STRETCH_SENSOR_PIN A0  // Analog pin for stretch sensor
#define FPSerial Serial1

DFRobotDFPlayerMini myDFPlayer;

int lastVolume = -1;  // Store last volume level to detect changes
const int minSensorValue = 2600;  // Value when sensor is NOT stretched
const int maxSensorValue = 2200;  // Value when sensor is fully stretched
const int minVolume = 0;   // Minimum volume
const int maxVolume = 30;  // Maximum volume

void setup() {
Serial.begin(115200);
FPSerial.begin(9600, SERIAL_8N1, /*rx =*/44, /*tx =*/43);

Serial.println(F("DFRobot DFPlayer Mini - Smooth Volume Control"));

if (!myDFPlayer.begin(FPSerial, /*isACK = */ true, /*doReset = */ true)) {
    Serial.println(F("Unable to begin DFPlayer! Check connections and SD card."));
    return;
}

Serial.println(F("DFPlayer Mini online."));

myDFPlayer.setTimeOut(2500);
myDFPlayer.volume(0);  // Start at volume 0 (silent)
myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
myDFPlayer.outputDevice(DFPLAYER_DEVICE_AUX);

Serial.println("Playing track 1...");
myDFPlayer.play(1);  // Start playing track 1
}

void loop() {
int sensorValue = analogRead(STRETCH_SENSOR_PIN);  // Read stretch sensor

// Apply a moving average filter to smooth sensor readings
static int smoothSensorValue = sensorValue;
smoothSensorValue = (smoothSensorValue * 0.8) + (sensorValue * 0.2);  // 80% old value, 20% new

// Map the smoothed sensor value to a smooth volume range (0 to 30)
int volume = map(smoothSensorValue, minSensorValue, maxSensorValue, minVolume, maxVolume);
volume = constrain(volume, minVolume, maxVolume);  // Ensure volume stays within range

Serial.print("Sensor Value: ");
Serial.print(sensorValue);
Serial.print(" | Smoothed: ");
Serial.print(smoothSensorValue);
Serial.print(" | Volume: ");
Serial.println(volume);

// Update volume only if it changes
if (volume != lastVolume) {
    lastVolume = volume;
    if (volume == 0) {
    myDFPlayer.pause();  // Pause when no stretch
    } else {
    myDFPlayer.volume(volume);  // Adjust volume smoothly
    myDFPlayer.start();  // Resume playing if paused
    }
}

delay(100);  // Faster response while keeping it smooth
}




PITCH CONTROL

To achieve pitch control, I will need an external audio processor or use pre-recorded audio files at different pitches, because the function playBackSpeed(speed) does not exist in the DFPlayer Mini library.

int speed = 150;  // Default speed (medium pitch)

if (myData.stretchLevel == 1) {
Serial.println("Light Stretch - Low Pitch");
speed = 100;  // Slow speed (lower pitch)
} else if (myData.stretchLevel == 2) {
Serial.println("Medium Stretch - Medium Pitch");
speed = 150;  // Default medium speed (medium pitch)
} else if (myData.stretchLevel == 3) {
Serial.println("High Stretch - High Pitch");
speed = 200;  // Fast speed (higher pitch)
}

myDFPlayer.playBackSpeed(speed);  // Apply speed to change pitch


wearable sculpture

The wearable sculpture is designed for two distinct purposes:

  • It embodies a key aspect of the sensory experience described in the graph and questionnaire. The participant noted: “The moment you enter the water, you feel the sharpness of crystal spikes piercing your skin. It hurts, but it also makes you feel alive.” The accessory captures this sensation, symbolizing the icy spikes felt when submerged in cold water.

  • It also serves a functional purpose. It is engineered to manage the receiver circuit and host the speaker, which will communicate wirelessly with the sender circuit.


I started by creating the 3D model shape for the wearable sculpture using Nomad Sculpt. This initial prototype was intended to help me understand several aspects:

  • The scale of both the ergonomic handle and the overall size of the piece.
  • The final aesthetic.
  • How to implement a system with an opening in the back that would be versatile, manageable, and spacious. This opening would also need integrated holes to allow sound from the speaker inside the bag to project outward.
  • Additionally, I needed a way to design the bag in separate parts to join later, since our lab's 3D printers cannot operate overnight. Because the bag would take more than two days to print, I planned to divide the final design into smaller sections that could be connected later.


prusa

printtest1

For my first print test, I used a white PLA filament and cut the design to print only the handle. This allowed me to evaluate whether the handle needed resizing or further ergonomic adjustments. You can find the 3D model for my first print test here1 and the Prusa Slicer file here2!

However, I applied too much support using the Prusa Slicer software, which made removing the supports after printing very time-consuming. Despite this, the handle turned out fine overall. Here are some alterations I need to make:

  • Consider adjusting the pinky finger hole to make it smaller, as it currently feels a bit uncomfortable.

  • Adjust the overall width of the sculpture to provide more space for the speaker.


sketch


3d


striped bio-plastic scarf

The striped bio-plastic scarf is designed to resemble the texture and appearance of water, by casting it on a holographic sheet.

The scarf is intended to be biodegradable, reflecting the transient nature of water—it comes and goes. This accessory serves as an experiment, aiming to introduce basic patterns, like stripes, into a bio-material. By hand-crafting it, I aim to explore its durability and potential applications.


For the first sample, I used tape to create walls on the mini tray I was using to cast the bio-plastic. I prepared gelatine bio-plastic. I created two different batches, adding green food coloring to one of them.

I began by pouring the uncolored bio-plastic into the side stripes, leaving the middle stripe empty. I let it dry for 15–20 minutes, and once it had partially hardened, I removed the tape walls and poured the green bio-plastic into the middle stripe. The green bio-plastic blended seamlessly with the other two, resulting in a striped sample.

striped


Recipe between Gelatine Bio-RESIN and Bio-SILICONE:

  • 48gr gelatine
  • 36gr glycerine
  • 240ml water
  • Ecoline for dyes
  • Straw to create air bubbles


striped


Step-by-Step Guide to Making a Striped Gelatine Bio-Plastic Scarf:

1. Prepare the Mold

Create a mold for casting the bio-plastic. I used a plastic sheet as the base and built walls around it using aluminum tape to achieve the desired size and shape of the scarf.

2. Create the Stripes

Use the aluminum tape again to mark out the stripes on the mold.

3. Prepare the Gelatine Bio-Plastic

Make your gelatine bio-plastic mixture and divide it into two separate containers. In one of them, mix in blue Ecoline pigment to create the color contrast for the stripes.

4. Pouring the Stripes

Pour the bio-plastic mixture one stripe at a time. Allow each stripe to dry slightly before adding the next one. Each time you pour a new stripe, remove the tape from the previous stripe to maintain clean lines.

5. Drying the Scarf

Once all the stripes are complete, let the entire scarf dry on the mold for 48 hours.

6. Shaping on a Mannequin

If you want to mold the scarf to a mannequin, do so after the initial 48-hour drying period while the material is still slightly flexible. Shape it as desired and let it continue drying until it is completely hardened.


scarf

crystallisation

cristal


crystal


Step-by-Step Guide to Crystallized Tights:

1. Prepare the Container - Choose a container large enough to fully submerge the tights in the alum-water solution without them touching the sides or bottom. To suspend the tights properly, I created a support system using wooden sticks across the top of the container. Sew a few stitches onto the tights and secure them to the sticks to prevent them from floating. Additionally, use vertical wooden sticks to keep them positioned toward the bottom without direct contact.

2. Prepare and Pour the Alum Solution - Carefully pour the alum-water solution into the container, ensuring the tights are fully submerged. Keep a close watch on the process—if you want a lighter crystal effect so the tights remain flexible, limit the submersion time to 25–40 minutes, depending on the concentration of your solution and the speed of crystal formation.

3. Create a Gradient Effect - To achieve a gradient of crystal growth, gradually remove certain sections of the tights from the solution at different times. This stops crystal formation on those areas while allowing the submerged parts to continue growing.

4. Opening the Tights - Once satisfied with the crystallization, remove the tights from the solution and immediately begin separating the crystalized sections to prevent them from sticking together. Carefully stretch them with your hands to break apart any connecting crystals, ensuring they remain wearable.

5. Dry and Seal - Let the tights dry completely. If you want to preserve the crystals for longer, consider applying a protective topcoat, such as clear nail polish.


cattails


half-fabrication files