A chandelier with a twist suspended within one of the alcoves in gates.

I've always had the sense that the center helix area of Gates felt a little empty. There is lots of open space and clear architecture but very little character to it. I thought an augmented reality sculpture would be the perfect addition to the place. I wanted to make something that felt classy and modern enough to fit into the aesthetic Gates is going for but also just a little bit quirky. I added the subtle movement of the particles to make the sculpture feel more compelling and catch the viewers eye for more than a passing moment. Overall, I would have liked to do a bit more with this project than I ended up doing, but struggled a much more with Unity than expected.


Activating GameObjects:

Awake and Start:


Conventions and Syntax:

Delta Time:


Enabling and Disabling Components:

Get Axis:

If Statements:



Look At:



Scope and Access Modifiers:

Behavior Components:

Switch Statements:

Translate and Rotate:

Update and FixedUpdate:

Variables and Functions:


There were a two points that stuck out with me about this talk, each of which I'll go over below:

  1. Relating generative language to exploration. I really liked Parrish's theme of space exploration throughout the talk, partially because it gave an exciting tone to the lecture, but mostly because it alluded to how much we can learn from generative writings. Parrish was astute in observing that since bots aren't constrained to norms the way that humans are, they can come up with things humans would be unable to in their current state.
  2. What is known and unknown is in the eye of the beholder. I really liked how Parrish talked about how what we deem as nonsense or sensical depends on who we listen to. Recent movements such as as #MeToo or Gay Marriage Equality would have been completely dismissed 100 years ago. Conversely, we now look back certain midcentury opinions that were held as facts and gawk. This shows the importance of both lending a voice to people that have none and maintaining an open mind.


Gender Bended Classics: Your favorite classics populated by a cast of familiar yet drastically different characters.

All files: yalbert-pdfs

Read a chapter here.

Overview: For this project, I wanted to explore gender expectations. In particular, I wanted to highlight how strongly held they can be without us even realizing it. In order to do this, I took classical texts, such as The Great Gatsby, Pride and Prejudice, and Mary Poppins, and switched the genders of all of the characters.

How I did it: If I wanted to create an ideal gender switching program, I would probably want to use some form of machine learning. However, a relatively simple find and replace algorithm, which I used for this project, works pretty well with minimal code. There are two main parts to my gender bending program, pronouns and names. The pronoun aspect is pretty straightforward. I simply compiled a dictionary of common pronouns (he : she, him : her, etc) and used it to switch out a word with it's opposite gender equivalent when I come across it in the text. There are still a few sticking points, but overall this works really well. The second element, name flipping, was significantly more difficult. Initially, I simply referenced a corpus of names to find the opposite gendered equivalent purely based on the Levenshtein distance between the original name and its opposite gendered candidate. However this lead to a lot of obscure names being used.

The effect it had: Even with an extremely imperfect gender switching algorithm, I'm really happy with the result of the project. You don't realize how little you associated men with nannies until you've read an excerpt from Marcus Poppins, or what the American dream means through the eyes of a woman until you've heard the tale of Jayla Gatsby. The wonderful thing about books is that the switch isn't immediately relevant. You'll will skim a more or less normal novel until you realize that something is off. Once you discover why the book seemed strange, you're left to wonder why you thought it was odd for a man to be a nanny or a woman to accrue wealth in order to win back a lost love.

Next steps: The algorithm, particularly the name replacements, still need a lot of work. I just found the US census results of the most popular names, arranged in ascending order, for every year since 1880. I'm trying to use this to generate more period relevant names that are selected based on both Levenshtein distance and popularity, instead of purely the former. I'd like to keep improving this algorithm until I can generate texts that are more convincing than what I currently have.

Text processing python script:

import random
from random import shuffle
from os import listdir
from os.path import isfile, join
punctuations = ["","'s", ".", ":", ",", "!", "?", ";"]
quotations = ["'", '"', "(", ")", '“','”']
splitters = {"\n", "-"}
def makeNameSets(year = 2017):
    path = "names/"
    files = [f for f in listdir(path) if isfile(join(path, f))]
    nameFile = getRightFile(year)
    contents = open("names/" + nameFile, "r")
    namesArr = contents.read().split("\n")
    femaleNames = dict()
    maleNames = dict()
    for namePkg in namesArr:
        sliced = namePkg.split(",")
        if(len(sliced) == 3):
            name, gender, pop = sliced
            if(gender == "F"):
                if(name[0] in femaleNames):
                    letterDict = femaleNames[name[0]]
                    letterDict = dict()
                    femaleNames[name[0]] = letterDict
                if(name[0] in maleNames):
                    letterDict = maleNames[name[0]]
                    letterDict = dict()
                    maleNames[name[0]] = letterDict
            letterDict[name] = int(pop)
    return(femaleNames, maleNames)  
def getRightFile(year):
    path = "/Users/Maayan/Google Drive/year 4.0 : senior fall/golan intermediate studio/07-book/gender flipper/names/"
    files = [f for f in listdir(path) if isfile(join(path, f))]
    files = sorted(files)
    return files[binSort(year, files, 1, len(files))]
def binSort(year, files, lowerInd, upperInd):
    midInd = (upperInd - lowerInd)//2 + lowerInd
    mid = int(files[midInd][3:7])
    if(mid == year):
        return midInd
    elif(mid < year): if(midInd == len(files)-1): return midInd else: return binSort(year, files, midInd, upperInd) else: if(midInd == 1): return midInd else: return binSort(year, files, lowerInd, midInd) def getNamesInNovel(contents, femaleNames, maleNames): names = dict() for word in contents: wordIters = [word] nameFound = None gender = None for i in range(1, 4): if(len(word) >i):
        for wordIter in wordIters:
            if(len(wordIter) != 0):
                firstLetter = wordIter[0]
            if firstLetter in femaleNames and wordIter in femaleNames[firstLetter].keys():
                curDict = femaleNames[firstLetter]
                if(curDict[wordIter] > 50):
                    nameFound = wordIter
                    gender = "f"
            if firstLetter in maleNames and wordIter in maleNames[firstLetter].keys():
                curDict = maleNames[firstLetter]
                if(curDict[wordIter] > 50):
                    nameFound = wordIter
                    gender = "m"
        if(nameFound != None):
            names[nameFound] = gender
    return names
def nameDictGenerator(contents, year):
    femaleNames, maleNames = makeNameSets(year)
    namesInNovel = getNamesInNovel(contents, femaleNames, maleNames)
    nameDict = dict()
    for name in namesInNovel.keys():
        if(namesInNovel[name] == "f"):
            nameSet = maleNames[name[0]]
            sameNameSet = femaleNames[name[0]]
            nameSet = femaleNames[name[0]]
            sameNameSet = maleNames[name[0]]
        closestName = findClosestName(name, nameSet, sameNameSet)
        if(name != "" and closestName != ""):
            addToDict(name, closestName, nameDict, True)
    return nameDict
def findClosestName(name, nameSet, sameNameSet):
    leastDist = None
    closestName = None
    closestNames = []
    maxDist = 3
    for otherName in nameSet:
        if(otherName in sameNameSet):
        if(len(name) > 0 and len(otherName) > 0 and name[0] != otherName[0]):
        dist = iterative_levenshtein(name, otherName)
        if(dist <= 3 and otherName): closestNames.append(otherName) elif(leastDist == None or leastDist > dist):
                leastDist = dist
                closestName = otherName
    if(len(closestNames) == 0):
        return closestName
        return findMostPopularName(closestNames, nameSet)
def findMostPopularName(closestNames, nameSet):
    mostPopName = None
    mostPopValue = None
    for name in closestNames:
        popValue = nameSet[name]
        if(mostPopValue == None or popValue > mostPopValue):
            mostPopValue = popValue
            mostPopName = name
    return mostPopName
def iterative_levenshtein(s, t, costs=(1, 1, 1)):
        iterative_levenshtein(s, t) -> ldist
        ldist is the Levenshtein distance between the strings 
        s and t.
        For all i and j, dist[i,j] will contain the Levenshtein 
        distance between the first i characters of s and the 
        first j characters of t
        costs: a tuple or a list with three integers (d, i, s)
               where d defines the costs for a deletion
                     i defines the costs for an insertion and
                     s defines the costs for a substitution
    rows = len(s)+1
    cols = len(t)+1
    deletes, inserts, substitutes = costs
    dist = [[0 for x in range(cols)] for x in range(rows)]
    # source prefixes can be transformed into empty strings 
    # by deletions:
    for row in range(1, rows):
        dist[row][0] = row * deletes
    # target prefixes can be created from an empty source string
    # by inserting the characters
    for col in range(1, cols):
        dist[0][col] = col * inserts
    for col in range(1, cols):
        for row in range(1, rows):
            if s[row-1] == t[col-1]:
                cost = 0
                cost = substitutes
            dist[row][col] = min(dist[row-1][col] + deletes,
                                 dist[row][col-1] + inserts,
                                 dist[row-1][col-1] + cost) # substitution
    return dist[rows-1][cols-1]
femaleNames, maleNames = makeNameSets()
def flipWholeText(textName):
    origText = open("texts/" + textName + ".txt","r")
    rawContents = origText.read()
    flippedContents = flip(rawContents)
    flippedText= open("flipped_texts/" + textName + "_flipped.txt","w+")
def flipExcerpt(textName, title, author, newName, year = 2018):
    origText = open("texts/" + textName + ".txt","r")
    rawContents = origText.read()
    excerptLen = 3000
    start = random.randint(0, len(rawContents) - excerptLen)
    end = start + excerptLen
    rawContents = title + "\nBy " + author + "\n" + rawContents[start:end]
    flippedContents = flip(rawContents)
    flippedText= open("../data/" + newName + ".txt","w+")
def customSplit(fullWord):
    minLen = None
    maxLen = None
    wordArr = [""]
    for char in fullWord:
        if(char in splitters):
            curSubstring = wordArr[-1]
            curSubstring = curSubstring + char
            wordArr[-1] = curSubstring            
    return wordArr
def customCombine(wordArr):
    word = ""
    for substring in wordArr:
        word = word + substring
    return word
def flip(rawContents, year = 2018): 
    contents = rawContents.split(" ")
    genDict = makeGeneralDict()
    nameDict = nameDictGenerator(contents, year)
    # replace any words
    for i in range(len(contents)):
        word = contents[i]
        wordArr = customSplit(word)
        for j in range(len(wordArr)):
            if(wordArr[j] != "" and wordArr[j] in genDict):
                wordArr[j] = genDict[wordArr[j]]
            if(wordArr[j] != "" and wordArr[j] in nameDict):
                wordArr[j] = nameDict[wordArr[j]]
        word = wordArr[0]
        word = customCombine(wordArr)
        contents[i] = word
    output = " ".join(contents)    
    return output
def dictInsert(word1, word2, d):
    words = []
    # add singular
    d[word1] = word2
    # add plural
    words.append(word1 + "s")
    d[word1 + "s"] = word2 + "s"
    # add capitals of those two
    for i in range(0, 2):
        word = words[i]
        word1 = word
        word2 = d[word1]
        d[word1.capitalize()] = word2.capitalize()
    # add punctuation
    for word in words:
        for punctuation in punctuations:
            word1 = word + punctuation
            word2 = d[word] + punctuation
            d[word1] = word2
            for quotation in quotations:
                if(quotation == '“'):
                    d[word1 + '”'] = word2 + '”'
                    d[quotation + word1] = quotation + word2
                    d[quotation + word1 + '”'] = quotation + word2 + '”'
                    d[word1 + quotation] = word2 + quotation
                    d[quotation + word1] = quotation + word2
                    d[quotation + word1 + quotation] = quotation + word2 + quotation
def addToDict(word1, word2, d, oneWay = False):
    dictInsert(word1, word2, d)
    if(oneWay == False):
        dictInsert(word2, word1, d)             
def makeGeneralDict():
    d = dict()
    addToDict("he", "she", d)
    addToDict("him", "her", d)
    addToDict("his", "hers", d)
    addToDict("his", "her's", d)
    addToDict("madam", "mister", d)
    addToDict("mr", "mrs", d)
    addToDict("mr", "ms", d)
    addToDict("brother", "sister", d)
    addToDict("aunt", "uncle", d)
    addToDict("mother", "father", d)
    addToDict("mom", "dad", d)
    addToDict("ma", "pa", d)
    addToDict("husband", "wife", d)
    addToDict("king", "queen", d)
    addToDict("gentleman", "lady", d)
    addToDict("gentlemen", "ladies", d)
    addToDict("prince", "pricess", d)
    addToDict("lord", "lady", d, True)
    addToDict("baron", "baroness", d)
    addToDict("miss", "mister", d)
    addToDict("daughter", "son", d)
    addToDict("man", "woman", d)
    addToDict("men", "women", d)
    addToDict("boy", "girl", d)
    addToDict("grandmother", "grandfather", d)
    addToDict("sir", "dame", d)
    addToDict("stepmother", "stepfather", d)
    addToDict("godmother", "godfather", d)
    addToDict("himself", "herself", d)
    addToDict("mss", "mister", d, True)
    addToDict("horseman", "horsewoman", d)
    addToDict("horsemen", "horsewomen", d)
    addToDict("wizard", "witch", d)
    addToDict("warlock", "witch", d, True)
    addToDict("businessman", "businesswoman", d)
    addToDict("businessmen", "businesswomen", d)
    # addToDict("warlock", "witch", d, True)
    return d
books = [("harry_potter", "Harry Potter", "J. K. Rowling"),
        ("alice_in_wonderland", "Alice's Adventures in Wonderland", "Lewis Carrol"),
        ("great_expectations", "Great Expectations", "Charles Dickens"),
        ("huckleberry_finn", "Adventures of Huckleberry Finn", "Mark Twain"),
        ("jane_eyre", "Jane Eyre", "Charlotte Bronte"),
        ("jekyll_hyde", "The Strange Case of Dr. Jekyll and Mr. Hyde", "Robert Louis Stevenson"),
        ("mary_poppins", "Mary Poppins", "P. L. Travers"),
        ("oliver_twist", "Oliver Twist", "Charles Dickens"),
        ("frankenstein", "Frankenstein", "Mary Shelley"),
        ("peter_pan", "Peter Pan", "J. M. Barrie"),
        ("pride_and_prejudice", "Pride and Prejudice", "Jane Austen"),
        ("sherlock_holmes", "The Adventures of Sherlock Holmes", "Sir Arthur Conan Doyle"),
        ("the_great_gatsby", "The Great Gatsby", "F. Scott Fitzgerald"),
        ("anna_karenina", "Anna Karenina", "Leo Tolstoy")]
def generateExcerpts(books):
    for i in range(14):
        corpus, title, author = books[i]
        flipExcerpt(corpus, title, author, str(i))

Basiljs layout script:

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
function draw() {
    margin = 70
    width = 432
    height = width*3/2
    files = ["0.txt", "1.txt",
    b.textFont("Baskerville", "Regular");
    b.textFont("Baskerville", "Bold");
    b.text("Gender Bended Classics", margin, margin*1.5, width-margin*2, 100);
    b.textFont("Baskerville", "Regular");
    b.text("Generated by Maayan Albert", margin, margin*3, width-margin*2, 100); 
    for(i = 0; i < files.length; i++){ file = files[i] content = b.loadString(file); headers = b.loadStrings(file); title = headers[0] author = headers[1] start = title.length + author.length + 1 + 1 end = 1000 firstPage = content.slice(start, end) b.textAlign(Justification.LEFT_ALIGN) b.textSize(12) b.textFont("Baskerville", "Regular"); b.text("Excerpt from:", margin, margin, width-margin*2, 100); b.textSize(24) b.textFont("Baskerville", "Bold"); b.text(title, margin, margin*1.5, width-margin*2, 100); b.textSize(12) b.textFont("Baskerville", "Regular"); if(title.length > 24){
            b.text(author, margin, margin*2.4, width-margin*2, 100);           
            b.text(author, margin, margin*2, width-margin*2, 100);
        b.textFont("Baskerville", "Regular");
        b.text(firstPage, margin, margin*3.5, 
                width- margin*2, height-margin*4.5);
        secondPage = content.slice(end)
        b.text(secondPage, margin, margin, width-margin*2, height-margin*2-margin*.5);
        b.textFont("Baskerville", "Regular");
        b.text(". . .", margin, height-margin*1.35, width-margin*2, height-margin*.5);


Project: Pinokio

Creator: Adam Ben-Dror, Joss Doggett

Year: 2012-2014

This is one of my favorite physical computing projects that I have come across. It stands out for a couple reasons. First, the simple yet charming concept of a moving lamp allowed the creators to do a lot with a little. Second, the project is executed and documented outstandingly. Third, the attention to detail, such as when the lamp turns itself back on after being turned off, is exceedingly clever and adds another dimension of perceived life to the item.

I also like how this project is highly interactive. It's wonderful to see the joy on the onlookers faces as they approach and play with the lamp. The concept of a moving lamp is nothing new. However, the way that the creator was able to bring it to life in a high fidelity and accessible manner is really admirable. Some day I aspire to make an interactive piece of art as compelling as this one.


For this project, we originally intended to make a Google Home-like device that reacted to speech input. We thought that if we used 2-3 microphones, we could triangulate the location of the user's voice. We wanted to use this information to make a robot that turned its head towards a user/person, and would reply with 3 pre-recorded soundbytes. Unfortunately, we really struggled to get useful inputs from the Arduino speakers and eventually had to shift directions.

We started looking at ways we could use the tools in front of us in interesting ways. In the physical computing labs where we were working, we played around with K'nex and pipe cleaners and light bulbs.

The light bulb was another point of challenge for us as we couldn't quite figure out how to power it correctly.

Ultimately, we focused on the servo, stepper motor, IR sensor to see what we could come up with. The K'nex pieces were especially great for interesting motion as they kind of operate as gears that only require 1 motor. We decided to lean in on these and create an interactive creature made up of K'nex. As you approach the object, it will flap its wings faster, either in fear or excitement for a new onlooker.


I really struggled to come up with a compelling idea for this project. Initially, I wanted to do something with hands using openpose. Unfortunately, it was too slow on my computer to use live.

After unsuccessfully attempting openpose, I shifted my focus to FaceOSC. I explore a couple different options, including a rolling ping pong ball, before settling on this. Initially inspired by a project Marisa Lu made in the class, I wanted to create a drawing tool where the controller is one's face. Early functionality brainstorms included spewing particles out of one's mouth and 'licking' such particles to move them around. Unfortunately, faceOSC's tongue detection system is not great so I had to shift directions.

Thinking back to the image processing project for 15-104, I thought it would be fun if the particles 'revealed' the user's face. Overall, I'm happy with the elegant simplicity of the piece. I like Char's observation when she described it as a 'modern age peephole'.However, I'm still working on a debug mode in case faceOSC doesn't read the mouth correctly.


ParticleSystem ps;
import processing.video.*;
Capture cam;
import oscP5.*;
OscP5 oscP5;
int found;
float[] rawArray;
Mouth mouth;
int particleSize = 3;
int numParticles = 20;
void setup() {
  size(640, 480);
  ps = new ParticleSystem(new PVector(width/2, 50));
  mouth = new Mouth();
void setupOSC(){
  rawArray = new float[132]; 
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "rawData", "/raw");
void setupCam(){
  String[] cameras = Capture.list();
  if (cameras.length == 0) {
    println("There are no cameras available for capture.");
  } else {
    println("Available cameras:");
    for (int i = 0; i < cameras.length; i++) {
    // The camera can be initialized directly using an 
    // element from the array returned by list():
    cam = new Capture(this, cameras[0]);
void draw() {
  if (cam.available() == true) {
  translate(width, 0);
  scale(-1, 1);
  ps.origin =new  PVector(mouth.x, mouth.y);
  if(mouth.isOpen || mouth.isBlowing){
  //image(cam, 0, 0);
void drawFacePoints() {
  int nData = rawArray.length;
  for (int val=0; val<nData; val+=2) {
      fill(100, 100, 100);
      ellipse(rawArray[val], rawArray[val+1], 11, 11);
// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles 
class ParticleSystem {
  ArrayList particles;
  PVector origin;
  ParticleSystem(PVector position) {
    origin = position.copy();
    particles = new ArrayList();
  void addParticle() {
    for(int i = 0; i < numParticles; i++){ particles.add(new Particle(origin)); } } void run() { for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = particles.get(i);
      if (p.isDead()) {
class Mouth{
 boolean isOpen;
 boolean isBlowing;
 float h;
 float w;
 float x;
 float y;
 float xv;
 float yv;
 void update(){
   PVector leftEdge = new PVector(rawArray[96], rawArray[97]);
   PVector rightEdge = new PVector(rawArray[108], rawArray[109]);
   PVector upperLipTop = new PVector(rawArray[102], rawArray[103]);
   PVector upperLipBottom = new PVector(rawArray[122], rawArray[123]);
   PVector lowerLipTop = new PVector(rawArray[128], rawArray[129]);
   PVector lowerLipBottom = new PVector(rawArray[114], rawArray[115]);
   float lastx = x;
   float lasty = y;
   w = rightEdge.x - leftEdge.x;
   x = (rightEdge.x - leftEdge.x)/2 + leftEdge.x;
   y = (lowerLipBottom.y - upperLipTop.y)/2 + upperLipTop.y;
   h = lowerLipBottom.y - upperLipTop.y;
   float distOpen = lowerLipTop.y - upperLipBottom.y;
   float avgLipThickness = ((lowerLipBottom.y - lowerLipTop.y) + 
                           (upperLipBottom.y - upperLipTop.y))/2;
   if(distOpen > avgLipThickness){ isOpen = true;}
   else { isOpen = false;}
   if(w/h <= 1.5){ isBlowing = true;}
   else { isBlowing = false;}
   xv = x - lastx;
   yv = y - lasty;
 void drawDebug(){
   if(isOpen || mouth.isBlowing){
       stroke(255, 255, 255, 150);
       ellipse(x, y, w, h);
// A simple Particle class
class Particle {
  PVector position;
  PVector velocity;
  PVector acceleration;
  float lifespan;
  Particle(PVector l) {
    acceleration = new PVector(0, 0.00);
    velocity = new PVector(random(-1, 1), random(-2, 0));
    position = l.copy();
    lifespan = 255.0;
  void run() {
  // Method to update position
  void update() {
    //lifespan -= 1.0;
    velocity.x = velocity.x *.99;
    velocity.y = velocity.y *.99;
  // Method to display
  void display() {
    //stroke(255, lifespan);
    //image(cam, 0, 0);
    float[] col = getColor(position.x, position.y);
    fill(col[0], col[1], col[2]);
    ellipse(position.x, position.y, particleSize,particleSize);
  // Is the particle still useful?
  boolean isDead() {
    if (lifespan < 0.0) { return true; } else { return false; } } } public float[] getColor(float x, float y){ cam.loadPixels(); int index = int(y)*width +int(x); float[] col = {0, 0, 0}; if(index > 0 && index < cam.pixels.length){
    col[0] = red(cam.pixels[index]);
    col[1] = green(cam.pixels[index]);
    col[2] = blue(cam.pixels[index]);
  return col;
public void rawData(float[] raw) {
  rawArray = raw; // stash data in array


Instructions: Click on the screen to build a creature that eats the dot. Work with your opponent, who has a different dot but is controlling the same creature.


Summary: An interactive game in which players are forced to work together to reach their goals.

Within my game, players must try to eat the dots placed around the canvas. While each players' dots are in different locations, they are all navigating using the same springular blob. Therefore, they have to work together to ensure everyone eats their dots. This project was an extension of past spring explorations that I've done. I really like the interactive twist though because it forces a sort of collaboration that people aren't always used to. Even though everyone has separate goals, they have to work together to collectively reach them. Based on the nature of the game, it is best played when the two players on are in the same room. If this isn't possible, the game could also be played with players talking on the phone or texting one another.


Name: inForm

Creators: Daniel Leithinger, Sean Follmer

Year: 2013


This is a project that came out of Hiroshi Ishii's incredible Tangible Media Lab. It explores how far we can push interactive surfaces and proves that they don't have to be confined to pixels on a screen. The possibilities of the concept really excite me. Imagine if every surface was covered in this material. Our future could contain interactive spaces that have a physicality to them in a way that doesn't exist in most contemporary visions of the future. One disappointing element of this project was that it was put out in 2013. I'm unsure if any improvements or compelling use cases have been created since then. I'd like to see this concept move beyond an interesting research project and into the public sphere as a consumer product, but I'm unsure when that will actually occur.