CONCEPT:

Masks Off is a project that investigates the proxy face or, in other words, the face upon the face. The masks that are constructed as a result of this investigation juxtapose the abstracted face of the wearer’s partner on top of that of the wearer and the partner’s environment upon the wearer. The mask takes data from the partner’s environment in order to activate the masks. In the case of Elise’s mask, the position of Anishwar’s head while wearing his mask sends data to Elise’s mask in order to activate the pneumatic and speaker of the Bluefruit. Anishwar’s mask is activated by the level of darkness in Elise’s environment which causes the pneumatic in his mask to inflate and the speaker on his Bluefruit to play tones.

The team decided to explore the concept of the proxy face and the juxtaposition of distant people and environments on another because they were interested in analyzing the significance and implications of the remote digital communication that has become popularized during the Covid-19 pandemic through the symbol of the mask which has taken on a new prominence and significance during this time period.

Concept Sketch

EXECUTION:

We decided to overlap tulle over fabric since the embroidery was much easier on the tulle. Elise stitched the designs into the tulle to create the faces and attached the tulle to the fabric underneath. For Anishwar, we used the lace ‘sheet’ that Elise sewed together. For Elise, we used the stretchy metallic fabric. The masks were pretty jarring in imagery and interesting at the same time.

Anishwar’ Mask
Elise’s Mask

PROCESS:

Our process begin with experimenting on what we wanted the masks to incorporate and how might they look. Anishwar drew sketches of ideas and the one below was what we ultimately went with. The idea was to create a proxy face on top of our own and involve lights and noise when reacting to the other’s environment. Our final tweaked those processes.

This image has an empty alt attribute; its file name is IMG_6738-584x1024.jpg
First Sketches

The idea for the masks came from the runway, looking at the work of John Galliano. You can see the influence in our work as we used tulle and bold colors as well as the black thread detailing in Anishwar’s mask. Elise’s mask also used embroidery detailing and tulle but in a red thread.

This image has an empty alt attribute; its file name is mask3.jpeg
This image has an empty alt attribute; its file name is mask-1-790x1024.jpeg

After gathering the basic idea, we chose to look at the working parts. We experimented with pneumatics before we got the motor pump. Once we got the motor, we also made sure to add holes to that the inflatables could relax between pumping cycles. The final inflatables were smaller round bladders.

This image has an empty alt attribute; its file name is IMG_5067-768x1024.jpg

We also experimented with the design aspects, looking at embroidery and the fabric that we could use. When making the final mask, the embroidery foot on the sewing machine was eating up the tulle and making holes, so we chose to do hand-stitching with embroidery floss when making the faces.

This image has an empty alt attribute; its file name is IMG_6744-1-1024x768.jpg
Anishwar’s Embroidery Experimentation
This image has an empty alt attribute; its file name is IMG_5114-768x1024.jpg
Lace used for Anishwar’s mask

After we figured out our working parts, we combined our efforts to create the final masks. We had a lot of trouble getting the code to work over the bridge and that took a lot of our time. The final masks also used a double layer of fabric, one with embroidery and the other to give more structure and color. The pneumatics were placed under the masks, either with elastic around the head or attached to the inside of the mask.

ANISHWAR’S CODE:

# cpb_pump_control.py

# Demonstrate control of an air pump based on a DC motor driven via a MOSFET
# transistor driver circuit *or* a DRV8833 motor driver in a single-ended mode.
#
#  1. The driver PWM input           connects to A2
#  2. The battery ground line        connects to GND

# Related documentation:
# https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html#module-pwmio
# https://circuitpython.readthedocs.io/projects/motor/en/latest/api.html#module-adafruit_motor.servo

# ----------------------------------------------------------------
# Import standard Python modules.
import time, math, sys

# Import the board-specific input/output library.
from adafruit_circuitplayground import cp

# Import the low-level hardware libraries.
import board
import pwmio
import supervisor

# ----------------------------------------------------------------
# Initialize hardware.

# Create a PWMOut object on pad A2 to generate control signals.
pwm = pwmio.PWMOut(board.A3, duty_cycle=0, frequency=20000)

# ----------------------------------------------------------------
# Initialize global variables for the main loop.

phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second
next_pwm_update = time.monotonic_ns()
next_status_update = time.monotonic_ns()
toneCheck = True
nextSound = time.monotonic_ns()
pitch = 329
pitchTwo = 293
nextPrint = time.monotonic_ns()
tokens = []
motionDuration = 0
soundDuration = 0
soundDurationTwo = 0
lastNow = time.monotonic_ns()
# ----------------------------------------------------------------
# Enter the main event loop.
while True:
    # Read the current integer clock.
    #print (pitch)
    now = time.monotonic_ns()
    soundNow = time.monotonic_ns()

    elapsedTime = now - lastNow
    lastNow = now

    x,y,z = cp.acceleration
    if now >= nextPrint:
        print(pwm.duty_cycle)
        nextPrint = now + 2000000000
        if x &gt; 1 or y < 0:
            print("start")
            if x &gt;= 1.25:
                print("go")
            elif y < 0:
                print("onward")
    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()
        tokens = line.split()
        if line.startswith("begi"):
            motionDuration = 7050000000
        if line.startswith("now"):
            soundDuration = 500000000
        if line.startswith("geronimo"):
            soundDurationTwo = 500000000
    # If the time has arrived to update the servo command signal:
    if soundDuration &gt; soundDurationTwo:
        soundDurationTwo = 0
    elif soundDurationTwo &gt; soundDuration:
        soundDuration = 0
    if motionDuration &gt; 0:
        if now &gt;= next_pwm_update:
            next_pwm_update += 20000000  # 20 msec in nanoseconds (50 Hz update)
            pwm_level = 0.5 + 0.5 * math.sin(phase_angle)

            # convert a unit-range (0 to 1) pwm_level to a 16-bit integer representing a fraction
            new_duty_cycle = min(max(int(pwm_level * 2**16), 0), 2**16-1)

            # If the new value is less than a reasonable minimum, clamp to zero.
            # The pump motor will stall if the PWM fraction is too low; this turns
            # it off instead.
            if new_duty_cycle < 48000:
                pwm.duty_cycle = 0
            else:
                pwm.duty_cycle = new_duty_cycle

            phase_angle = (phase_angle + phase_rate * 0.01) % (.5 * math.pi)

        # If either button is pressed, override the generated servo signal and run the motor.
        if cp.button_a or cp.button_b:
            pwm.duty_cycle = 2**16-1

        # If the time has arrived to display the status:
        if now &gt;= next_status_update:
            next_status_update += 500000000  # 0.5 sec in nanoseconds (2 Hz update)
        motionDuration -= elapsedTime
    else:
        pwm.duty_cycle = 0



    if soundDuration &gt; 0:
        if soundNow &gt;= nextSound:
            if pitch == 329:
                pitch = 493
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
            elif pitch == 493:
                pitch = 329
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
            soundDuration -= elapsedTime
    elif soundDurationTwo &gt; 0:
        if soundNow &gt;= nextSound:
            if pitchTwo == 293:
                pitchTwo = 466
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
            elif pitchTwo == 466:
                pitchTwo = 293
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
            soundDurationTwo -= elapsedTime
    else:
        cp.stop_tone()

ELISE’S CODE:

# https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html#module-pwmio
# https://circuitpython.readthedocs.io/projects/motor/en/latest/api.html#module-adafruit_motor.servo
import time, math, sys
from adafruit_circuitplayground import cp
import board
import pwmio
import supervisor
pwm = pwmio.PWMOut(board.A3, duty_cycle=0, frequency=20000)
phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second
next_pwm_update = time.monotonic_ns()
next_status_update = time.monotonic_ns()
toneCheck = True
nextSound = time.monotonic_ns()
pitch = 329
pitchTwo = 293
nextPrint = time.monotonic_ns()
motionDuration = 0
soundDuration = 0
soundDurationTwo = 0
lastnow = time.monotonic_ns()

while True:
    # Read the current integer clock.
    #print(cp.light)
    now = time.monotonic_ns()
    soundNow = time.monotonic_ns()

    elapsed_time = now - lastnow
    lastnow = now

    if now &gt;= nextPrint:
        nextPrint = now + 2000000000
        if cp.light < 100:
            print("begi")
            if cp.light < 81 and cp.light &gt;= 21:
                print("now")
            elif cp.light < 20:
                print("geronimo")
        else:
            print("bright")


    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()
        #print(line)
        #tokens = line.split()
        if line.startswith("start"):
            motionDuration = 9000000000
        if line.startswith("go"):
            soundDuration = 5000000000
        if line.startswith("onward"):
            soundDurationTwo = 5000000000
        # If the time has arrived to update the servo command signal:
    if motionDuration &gt; 0:
        if now &gt;= next_pwm_update:
            next_pwm_update += 20000000  # 20 msec in nanoseconds (50 Hz update)
            pwm_level = 0.5 + 0.5 * math.sin(phase_angle)

            # convert a unit-range (0 to 1) pwm_level to a 16-bit integer representing a fraction
            new_duty_cycle = min(max(int(pwm_level * 2**16), 0), 2**16-1)

            # If the new value is less than a reasonable minimum, clamp to zero.
            # The pump motor will stall if the PWM fraction is too low; this turns
            # it off instead.
            if new_duty_cycle < 48000:
                pwm.duty_cycle = 0
            else:
                pwm.duty_cycle = new_duty_cycle

            phase_angle = (phase_angle + phase_rate * 0.020) % (1 * math.pi)

        # If either button is pressed, override the generated servo signal and run the motor.
        if cp.button_a or cp.button_b:
            pwm.duty_cycle = 2**16-1

        # If the time has arrived to display the status:
        if now &gt;= next_status_update:
            next_status_update += 500000000  # 0.5 sec in nanoseconds (2 Hz update)
            #print(pwm.duty_cycle)
        motionDuration -= elapsed_time
    else:
        pwm.duty_cycle = 0
        cp.stop_tone()
    if soundDuration &gt; soundDurationTwo:
        soundDurationTwo = 0
    elif soundDurationTwo &gt; soundDuration:
        soundDuration = 0

    if soundDuration &gt; 0 and soundDurationTwo == 0:
        if soundNow &gt;= nextSound:
            if pitch == 329:
                pitch = 493
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
            elif pitch == 493:
                pitch = 329
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
        soundDuration -= elapsed_time
    elif soundDurationTwo &gt; 0 and soundDuration == 0:
        if soundNow &gt;= nextSound:
            if pitchTwo == 293:
                pitchTwo = 466
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
            elif pitchTwo == 466:
                pitchTwo = 293
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
        soundDurationTwo -= elapsed_time
    else:
        cp.stop_tone()

REFLECTION:

If there was more time, the team would have liked to make more masks and potentially create an installation with multiple of these masks being animated and producing sound together. It would have been interesting to explore this current iteration of the project in different contexts. The primary location for testing the final version of this project was the College of Fine Arts building; however, it would be interesting to see how the masks would be affected if used outside or in vastly more distant locations.