All posts by Ethan Printz

Many – Space Invaders Enemies

In thinking about what types of systems require both looping, objects, and arrays, one of the primary examples that kept coming back to my mind was the logic behind repeating video game enemies. As I was making a space-invaders like game for ComLab anyway, I thought that this would be a good opportunity to explore that area. Here is the code that would be generating and subsequently controlling the enemies to that they follow a repeatable path, all this needs to work is a div with the id “enemies” somewhere within the html code for the page.

// Tracks amount of enemies    
        var enemies = [];
    // States of enemy movement
    // Possible states: down, up, left
        var enemyState = [];
        var previousEnemyState = [];
    // Distance traveled left
        var distanceLeft = []
    // Enemy Speed
        var enemySpeed = 5;
//Create Enemy Ships
        function createEnemy(){
            for(i=0;i<waveTotals.length;i++){
                if(currentlyWave){
                    for(var n=0;n<waveTotals[i];n++){
                        setTimeout(function(){
                            enemies.push(n);
                            enemyState.push("down");
                            previousEnemyState.push("down");
                            distanceLeft.push(0);
                        },1000*n);
                        $("#enemies").append("<div class='enemy' id='enemy" + n + "'></div>");
                    }
                    currentlyWave = false;
                    currentWave++;
                }
            }
        }
    // Move Enemy Ships
        function moveEnemy(){
            for(var i=0;i<enemies.length;i++){
                console.log(enemies.length);
                console.log("i:"+i);
                // If the i-th element is traveling downwards
                    if(enemyState[i]=="down"){
                        var enemyTop = $("#enemy"+i).position().top;
                        enemyTop+=enemySpeed;
                        $("#enemy"+i).css("top",enemyTop);
                            if(enemyTop>=(window.innerHeight-distanceFromEdges)){
                                enemyState[i]="left";
                                previousEnemyState[i]="down";
                            }
                // If the i-th element is traveling to the left
                    }else if(enemyState[i]=="left"){
                        var enemyLeft = $("#enemy"+i).position().left;
                        enemyLeft-=enemySpeed;
                        distanceLeft[i]+=enemySpeed;
                        //If done traveling to the left and previously was down
                            if(distanceLeft[i]>=distanceToTravelLeft && previousEnemyState[i]=="down"){
                                enemyState[i]="up";
                                distanceLeft[i]=0;
                                previousEnemyState[i]="left";
                            }
                        //If done traveling to the left and previously was up
                            if(distanceLeft[i]>=distanceToTravelLeft && previousEnemyState[i]=="up"){
                                enemyState[i]="down";
                                distanceLeft[i]=0;
                                previousEnemyState[i]="left";
                            }
                        $("#enemy"+i).css("left",enemyLeft);
                // If the i-th element is traveling up
                    }else if(enemyState[i]=="up"){
                        var enemyTop = $("#enemy"+i).position().top;
                        enemyTop-=enemySpeed;
                        $("#enemy"+i).css("top",enemyTop);
                            if(enemyTop<=distanceFromEdges){
                                enemyState[i]="left";
                                previousEnemyState[i]="up";
                            }

                    }
            }
        }
.enemy{
        width: 6vh;
        height: 6vh;
        position: absolute;
        z-index: 11;
        top: -10vh;
        right: 2vh;
        border: 10px solid #C1272D;
        box-sizing: border-box;
        -moz-box-sizing: border-box;
        -webkit-box-sizing: border-box;
    }
<div id="enemies"></div>

 

Sound and Light

For this project, I went back and added audio-amplitude-based width calculations to my original concentric circle “Visual Elements” project. While this was just a fun little side project to get more familiar with using mic input, I wanted to do a similar width modification with sound volume for the drama innovation lab side wall projections in real time during the show, so this was a cool way to start to approach that task. In the future I want to figure out how to make this concept look more fluid with some sort of transition time between sizes.

Concentric Circles Responding to Audio Volume

var ellipseWidth = 1;
var ellipseRadius = 1;

function setup() {
  
  createCanvas(windowWidth, windowHeight);
  
  // Create an Audio input
  mic = new p5.AudioIn();

  // start the Audio Input.
  // By default, it does not .connect() (to the computer speakers)
  mic.start();
  
}

function draw() {
  
  var vol = (mic.getLevel()*1000)+1;
  console.log(vol);
  
  background(30);
  
	for(var i=0;i<100;i++){
    
    //Draw Outer Ellipses
      fill(30);
      stroke(240);
    	ellipseRadius = (ellipseWidth%i)*4*vol;
      ellipse((windowWidth / 2), (windowHeight / 2), ellipseRadius, ellipseRadius);
    
  	//Draw Inner Ellipses
      stroke(255,0,0);
    	ellipseRadius = (ellipseWidth%i)*3*vol;
  		ellipse((windowWidth / 2), (windowHeight / 2), ellipseRadius, ellipseRadius);
    
  }

  ellipseWidth++;
  
  if(ellipseWidth>920){
    ellipseWidth=1;
  }
  
}

Editable: https://editor.p5js.org/ethanprintz/sketches/SJ2mTiYc7

Full Screen: https://editor.p5js.org/ethanprintz/full/SJ2mTiYc7

Tangible Course Search – Final Documentation

Introduction

For my final project, I created a combined physical and digital interface for the course search that used physical buttons, dials, and faders to filter through manually created JSON files of the most popular courses/majors available to IMA students. This is a noted change from the start of the projection, as it was originally intended to use a data-scrapping API to search through all the courses for all majors at NYU, both undergraduate and graduate.

Important Note: Unfortunately I have left my photographs and videos of the physical prototype on my DSLR’s SD card back in my dorm room and am now back at home in California, so there won’t be any pictures in this final documentation for now until I get back from break and am able to offload them from the SD card. 

Original Flowchart

User Flow 1

Final Flowchart

User Flow 2

Changes Since Prototype

The most notable change since the prototype of the projection has been adding the increased filtering controls that allowed control of time, credits, and a randomizer. Here is the isolated HTML code for the digital interface of those additions:

<div id="filterScreen">
        <div id="filterTitle"></div>
        <div id="creditFilter">
            <div id="creditFilterVertical"></div>
        </div>
        <div id="timeFilter">
            <div id="clock">
                <div class="hand" id="firstHand"></div>
                <div class="hand" id="secondHand"></div>
            </div>
        </div>
        <div id="dateFilter">
            <div id="monday"><span class="dayTag">MON</span><span class="dayValue">✓</span></div>
            <div id="tuesday"><span class="dayTag">TUE</span><span class="dayValue">✓</span></div>
            <div id="wednesday"><span class="dayTag">WED</span><span class="dayValue">✓</span></div>
            <div id="thursday"><span class="dayTag">THU</span><span class="dayValue">✓</span></div>
            <div id="friday"><span class="dayTag">FRI</span><span class="dayValue">✓</span></div>
        </div>
        <div id="randomFilter">
            <img src="img/diceIcon.png" alt="Dice Icon">
        </div>
        <div id="masterConfirm"></div>
    </div>

And here’s the function that animates the change over from the major selection screen (what used to be the career select screen)

function careerDialConfirmed(){
        if(careerConfirmValue){
            if(stage == 0){
                console.log("hello!");
                $("#chooseCareer, #dialBackground").animate({
                    left: "-50vw",
                    opacity: "0"
                },2000);
                $("#chooseDial").animate({
                    left: "-20vw",
                    opacity: "0"
                },100);
                $("#confirmCareer").css({
                    "background-position": "left bottom",
                    "top":"40vh"
                });
                $("#confirmCareer").css({
                    "top":"100vh"
                });
                var filterScreen = $("#filterScreen");
                setTimeout(function(){
                    filterScreen.css({
                        "opacity":"1",
                        "pointer-events":"auto"
                    });
                },2000);
            }
        }
    }

Another major change since the first prototype was changing the career selection criteria from all-university careers to simply IMA-related majors (IMA, IDM, ITP, MCC, and Open Arts) to take courses in. This change was put in place because:

a) Once the data scrapping side of the project failed, it was no longer feasible to show many different majors as it would all require manual creation of the JSON files. I needed to figure out a way to restrict the amount of data the user could theoretically see

b) When presenting my project to various people I got many comments about wanting to filter by major, and I didn’t have any good method other than just putting a keyboard in front of them and calling it a day. I felt this went against the dashboard-like interface for the project. So instead I limited the number of possible majors to what could be fit onto a dial and used that.

Thankfully, because of the way that I programmed the javascript and CSS before to be irrespective of the content of the dial buttons (mostly so I can use this code again in the future), all I needed to do was change the HTML and add a couple Javascript variables for the final filtering mechanism.

<div id="confirmCareer">
        <div class="confirm-right"></div>
    </div>
    <div id="dialBackground"></div>
    <div id="chooseCareer">
        <div id="chooseMCC" class="chooseItem">MCC</div>
        <div id="chooseIDM" class="chooseItem">IDM</div>
        <div id="chooseIMA" class="chooseItem">IMA</div>
        <div id="chooseITP" class="chooseItem">ITP</div>
        <div id="chooseTOA" class="chooseItem">TOA</div>
    </div>
    <div id="chooseDial" class="dial">
        <div class="arrow-right"></div>
    </div>

The CSS stayed the same because it used nth-Child for its margins:

.chooseItem{
            text-align: center;
            color: #999999;
            font-size: 1.7vw;
            cursor: pointer;
            font-weight: bold;
            border-radius: 1vw;
            padding: 0.5vw;
            width: auto;
            transition: all 1s ease-in-out;
        }
            .chooseItem:nth-child(1){
                margin-left: 8vw;
            }
            .chooseItem:nth-child(2){
                margin-left: 14vw;
            }
            .chooseItem:nth-child(3){
                margin-left: 16vw;   
            }
            .chooseItem:nth-child(4){
                margin-left: 14vw;
            }
            .chooseItem:nth-child(5){
                margin-left: 8vw; 
            }

For the physical construction of the final interface I decided to go with cardboard instead of wood. This allowed for more rapid modifications should anything go wrong or needed to be changed, and also meant that I could work away from the busy shop and without the perpetually used shop equipment during this pre-show stretch of time. It did however mean that I needed to get creative with the way I made dials and faders.

Digital Selfie

I created a quick minimalist ‘selfie’ that mimics the SVG avatar I used for my IMA application website. I also wanted to make sure that it scales correctly with window size, so all height, width, and position values are made as modifications of the window height (h).

P5 Code

function setup() {
  createCanvas(windowWidth, windowHeight);
}

function draw() {
  var w = windowWidth;
  var h = windowHeight;
  background(20);
  noStroke();
  
  fill(90,75,66);
  ellipse((w/2)-(h/8),h/4,h/4);
  
  fill(193,180,154);
  rect((w/2)-(h/5.4),h/3.5,h/8,h/6);
  
  fill(211,191,168);
  ellipse((w/2)-(h/8),h/3.7,h/4);
  
  fill(211,191,168);
  ellipse((w/2)-(h/8),h/3.7,h/4);
  
  fill(25,113,180);
  ellipse((w/2)-(h/8),h/1.7,h/3);
  rect((w/2)-(h/3.48),h/1.8,h/3.06,h/3.06);
}

Final Product
Portrait

Original Inspiration

Person Waving

Calling the Future: Multi-Format Color Picker

For my project, I decided to do a simple enough color picker with the twist that it shows hex, rgb and hsl values for the color decided through the rgb sliders in real time. This required borrowing and crediting the Javascript code necessary to quickly convert between the formats. There was also the difficulty of figuring out how to properly change the center text so it will always be visible upon the background. I originally attempted this through javascript, but ended up finding a simple enough CSS-only solution for it involving text clipping and filtering.

GIF Showing Color Picker

PS In the GIF the compression makes it look like the sliders pop in and out of existence. They do not do that in person.

Here’s the code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Calling the Future</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="script.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="rgbSliders">
            <input type="range" min="0" max="255" value="50" class="slider" id="rSlider">
            <input type="range" min="0" max="255" value="50" class="slider" id="gSlider">
            <input type="range" min="0" max="255" value="50" class="slider" id="bSlider">
    </div>
    <div id="hexCode"></div>
    <div id="hexRGBCode"></div>
    <div id="hexHSLCode"></div>
</body>
</html>
@import url('https://fonts.googleapis.com/css?family=Fira+Mono:400,500,700');

html,body{
    margin: 0;
    padding: 0;
    width: 100vw;
    height: 100vh;
    overflow: hidden;
    background-color: grey;
    font-family: 'Fira Mono', monospace;
    font-weight: bold;
}
#rgbSliders{
    width: 20vh;
    height: 14vh;
    position: absolute;
    top: calc(50vh + 10vh);
    left: calc(50vw - 10vh);
}
    .slider {
        -webkit-appearance: none;  
        appearance: none;
        width: 100%; 
        height: 10px; 
        background: rgb(20,20,20);
        outline: none; 
        opacity: 0.7; 
        -webkit-transition: .2s; 
        transition: opacity .2s;
        border-radius: 12%; 
        margin: 2vh;
    }
        .slider:hover {
            opacity: 1; 
        }
        .slider::-webkit-slider-thumb {
            -webkit-appearance: none; 
            appearance: none;
            width: 25px; 
            height: 25px; 
            cursor: pointer;
            border-radius: 50%; 
            border: 5px solid rgb(20,20,20);
        }
            #rSlider::-webkit-slider-thumb{background: red}
            #gSlider::-webkit-slider-thumb{background: green}
            #bSlider::-webkit-slider-thumb{background: blue}

#hexCode{
    width: 20vw;
    height: 8vh;
    font-size: 6vh;
    text-align: center;
    box-sizing: border-box;
    position: absolute;
    top: calc(50vh - 4vh);
    left: calc(50vw - 10vw + 1vh);
    background: inherit;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-align: center;
    filter: invert(1) grayscale(1) contrast(9);
}
#hexRGBCode{
    width: 20vw;
    height: 4vh;
    font-size: 1.8vh;
    text-align: center;
    box-sizing: border-box;
    position: absolute;
    top: calc(50vh + 4vh);
    left: calc(50vw - 10vw + 1vh);
    background: inherit;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-align: center;
    filter: invert(1) grayscale(1) contrast(9);
}
#hexHSLCode{
    width: 20vw;
    height: 4vh;
    font-size: 1.8vh;
    text-align: center;
    box-sizing: border-box;
    position: absolute;
    top: calc(50vh + 7vh);
    left: calc(50vw - 10vw + 1vh);
    background: inherit;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-align: center;
    filter: invert(1) grayscale(1) contrast(9);
}
// Update Values
    window.setInterval(function(){
        var rColor = $("#rSlider").val();
        var gColor = $("#gSlider").val();
        var bColor = $("#bSlider").val();
        var backgroundColor = "rgb(" + rColor + ","  + gColor + "," + bColor + ")";
            $("body").css("background-color",backgroundColor);
        
        var hexColor = rgb2hex("rgba(" + rColor + ","  + gColor + "," + bColor + ")");
            $("#hexCode").html(hexColor);

        var hexRGBCode = "rgb(" + rColor + ","  + gColor + "," + bColor + ")";
            $("#hexRGBCode").html(hexRGBCode);

        var hexHSLCode = rgbToHsl(rColor,gColor,bColor);
            $("#hexHSLCode").html(hexHSLCode);
    },10);

// Sourced From https://jsfiddle.net/Mottie/xcqpF/1/light/
    //Function to convert rgb format to a hex color
        function rgb2hex(rgb){
            rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
            return (rgb && rgb.length === 4) ? "#" +
            ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
            ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) +
            ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
        }   

// Sourced From https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
    //Function to convert rgb format to a hsl color
        function rgbToHsl(r, g, b){
            r /= 255, g /= 255, b /= 255;
            var max = Math.max(r, g, b), min = Math.min(r, g, b);
            var h, s, l = (max + min) / 2;
        
            if(max == min){
                h = s = 0; // achromatic
            }else{
                var d = max - min;
                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
                switch(max){
                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                    case g: h = (b - r) / d + 2; break;
                    case b: h = (r - g) / d + 4; break;
                }
                h /= 6;
            }
        
            return "hsl(" + String(h).substring(0,4) + "," + String(s).substring(0,4) + "," + String(l).substring(0,4) + ")";
        }

 

Cultural Evolution

Communications systems typically evolve both greater abstraction and greater complexity over time. This abstraction makes the communications systems increasingly powerful, as one is able to apply the same base components to ever greater amounts of informational input and output. With these added responsibilities, however, also comes increasing complexity in the system. The future of communication will be one heavily influenced by increasing globalization and widening dispersal of technology. There will be a trend towards increased interconnectedness between different languages, both physical and digital.

Who

I feel that the response to this answer is highly dependent on whether the data held within the internet is placed under the ‘computer’ mentioned in the questions. If we do utilize the internet in our definition, then what we ‘look like’ to a computer would be a sum total of all of our interactions and what the algorithmic artificial intelligences could extrapolate based on those limited interactions. The notably leaves out much of our daily interactions with each other as well as our emotional responses to stimuli. An ideally inclusive device must be able to adapt to these parameters in real time, incorporating full natural language processing and image processing and data-based calculations into a singular entity that can examine interactions in their entirety.

Data: MTA API

For this project, I practiced using MTA’s Transit API, containing train timetables, stops, real-time incident reporting, and more. Accessing the API was fairly easy, as the account required didn’t necessitate stating a reason for access and automatically approves all accounts created within a minute. What was more difficult was actually working with the API, as it uses a relatively complicated but powerful GTFS model developed by Google to standardize tracking between transit systems for Google Maps. Luckily, after quite a bit of messing around myself, I found an existing library that was able to be implemented in a Node server that interfaced with the object structure. Here was a small test app I made that output a northbound timetable for station id 635.

var MTA = require('mta-gtfs');

var mta = new MTA({

     key:'8f70b45180beaf6c972c2c098da6f426', // only needed for mta.schedule() method

     feed_id:1// optional, default = 1

});

mta.schedule(635, 1).then(function (result) {

console.log(result['schedule']['635']['N']);

});

This output a series of objects:

[ { routeId: '5',
delay: null,
arrivalTime: 1545056100,
departureTime: 1545056100 },
{ routeId: '6',
delay: null,
arrivalTime: 1545056370,
departureTime: 1545056370 },...]

Automatic If Statement

In their current state, algorithms run our lives through silently organizing and personalizing the data feeds in which we spend our time and learn about our world. The decide on the news we see, the friends we find, and the information we research.  In the future this role in determining societal communications and information distribution will only grow as more of our daily life falls into technological domains in which algorithms govern discourse. These algorithms are made by  teams and committees of software engineers and researchers at major tech companies, meaning that first and foremost they are crafted with a focus on company profit rather than societal good.

Tangible Course Search – Proof of Concept

User Flow Diagram

Revised user flow

Physical Interface

I kept the physical interface relatively simple for this prototype, as most of the work has been done on programming the digital interface and connecting the physical to the digital. It’s currently just a simple potentiometer connected to the Arduino at Analog port 2. The plan is to 3D print a ‘top’ to the potentiometer (along these lines) to make the component more welcoming to the user.

Server (Physical to Digital)

For the server, I lightly redid my previous Node + Johnny-Five application to allow for the needed expandability for adding a ton of data sources. Now all of the variables are declared ahead of time and  the AJAX response is compacted into a single JSON object that is sent to the client. I tested the potentiometer values to ensure that they were being sent to the client correctly.I’ve also done some work on the backend of the course search with my team, though that isn’t included in the post as it isn’t a part of the project yet.

//---------------------------------------------
// Variable Declaration
//---------------------------------------------
//Career Dial
    const careerPotentiometerPin = 'A2';
    var careerPotentiometerValue = 0; // 0 - 1023

//---------------------------------------------
// Node Server Setup Code
//---------------------------------------------
// Module Requirements
var express = require('express');
var path = require('path');
var app = express();

// Set public folder for client-side access
app.use(express.static('public'));

// Send index.html at '/'   
app.get('/', function(req, res){
    res.sendFile(path.join(__dirname + '/views/index.html'));
});

//Send AJAX data stream at '/data'
app.get('/data', function(req,res) {
    // Compile individual variables into object
        var dataToSendToClient = {
            'careerPotentiometerValue': careerPotentiometerValue
        };
        
    // Convert javascript object to JSON
        var JSONdata = JSON.stringify(dataToSendToClient);
        
    //Send JSON to client
        res.send(JSONdata);
 });
 
//Set app to port 8080
app.listen(8080);

//Log start of app
console.log("App Started");

//---------------------------------------------
// Johnny-Five Code
//---------------------------------------------
var five = require("johnny-five"),
  board, potentiometer;

board = new five.Board();

board.on("ready", function() {

  // Create a new `potentiometer` hardware instance.
    potentiometer = new five.Sensor({
        pin: careerPotentiometerPin,
        freq: 250
    });

  // "data" get the current reading from the potentiometer
    potentiometer.on("data", function() {
        console.log("Career Potentiometer: " + this.value);
        careerPotentiometerValue = this.value;
    });
});

Digital Interface

As part of the prototype, I have finished coding the first component of the digital interface: the Academic Career selection. It consists of a simple dial that rotates based off of the potentiometer values received to select different careers as classified by Albert. All data is meant to be manipulated physically, so there are no digital mouse targets to allow for manipulation.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Tangible Course Search</title>
    <link rel="stylesheet" href="/css/style.css">
    <script src="/lib/jquery.js"></script>
    <script src="/js/script.js"></script>
</head>
<body>
    <!--<div id="header">Tangible Course Search</div>-->
    <div id="dialBackground"></div>
    <div id="chooseCareer">
        <div id="chooseMed" class="chooseItem">Medical</div>
        <div id="chooseGrad" class="chooseItem">Graduate</div>
        <div id="chooseUndergrad" class="chooseItem">Undergraduate</div>
        <div id="chooseLaw" class="chooseItem">Law</div>
        <div id="chooseDentist" class="chooseItem">Dentistry</div>
    </div>
    <div id="chooseDial" class="dial"><div class="arrow-right"></div></div>
</body>
</html>
//On Document Ready
$(document).ready(function(){

    //Log to check onload
        console.log('JQuery Loaded');

    //Send AJAX call to server every 20ms
        setInterval(function() {
                $.ajax({
                    url : 'http://localhost:8080/data',
                    type : 'GET',
                    dataType:'json',
                    // On Request Success
                        success : function(data) {
                            // Loop through attributes of given JSON Object
                            // to deconstruct object into variables
                                for (var property in data) {
                                    // Set old property value as var to track change
                                        window[property  + 'Old'] = window[property];                             
                                    // Set property name as var equal to property
                                        window[property] = data[property];
                                }              
                        },
                    // On Request Error
                        error : function(request,error) {
                            console.log("Request: "+JSON.stringify(request));
                        }
                });
            }, 20);

    //Change Values On-Screen every 20 ms
        setInterval(function() {
            changeCareerDial(careerPotentiometerValue, careerPotentiometerValueOld);
        }, 20);

});

//------------------------------------------------------------------------
// Function Declaration
//-----------------------------------------------------------------------
// Change Career Dial Elements
    function changeCareerDial(potentiometerValue, potentiometerValueOld){
        //If the potentiometer value has changed
            if(potentiometerValue != potentiometerValueOld){
                // 1| 0 to 205
                    if(potentiometerValue>=0 && potentiometerValue<205){
                        $('.chooseItem').removeClass('chooseSelected');
                        $('#chooseMed').addClass('chooseSelected');
                        $("#chooseDial").css({'transform' : 'rotate(-55deg)'});
                    }
                // 2| 206 to 411
                    else if(potentiometerValue>=206 && potentiometerValue<411){
                        $('.chooseItem').removeClass('chooseSelected');
                        $('#chooseGrad').addClass('chooseSelected');
                        $("#chooseDial").css({'transform' : 'rotate(-35deg)'});
                    }
                // 3| 412 to 617
                    else if(potentiometerValue>=412 && potentiometerValue<617){
                        $('.chooseItem').removeClass('chooseSelected');
                        $('#chooseUndergrad').addClass('chooseSelected');
                        $("#chooseDial").css({'transform' : 'rotate(0deg)'});
                    }
                // 4| 618 to 823
                    else if(potentiometerValue>=618 && potentiometerValue<823){
                        $('.chooseItem').removeClass('chooseSelected');
                        $('#chooseLaw').addClass('chooseSelected');
                        $("#chooseDial").css({'transform' : 'rotate(35deg)'});
                        
                    }
                // 5| 824 to 1023
                    else if(potentiometerValue>=824  && potentiometerValue<1023){
                        $('.chooseItem').removeClass('chooseSelected');
                        $('#chooseDentist').addClass('chooseSelected');
                        $("#chooseDial").css({'transform' : 'rotate(55deg)'});
                        
                    }
            }
    }
/*---------------------------------------------*/
/* Animations */
/*---------------------------------------------*/
@keyframes dialBreathe {
    0% { width: 150vh; height: 150vh; top: -25vh}
    50% { width: 155vh; height: 155vh; top: -27.5vh}
    0% { width: 150vh; height: 150vh; top: -25vh}
}

/*---------------------------------------------*/
/* Styles */
/*---------------------------------------------*/
html,body{
    margin: 0;
    padding: 0;
    background-color: #1f1d1d;
    font-family: sans-serif;
    width: 100vw;
    height: 100vh;
    overflow: hidden;
}
    #header{
        color: #999999;
        font-size: 2vw;
        position: absolute;
        top: 1vw;
        left: 1vw;
        font-weight: bold;
    }
    #chooseCareer{
        position: absolute;
        top: calc(50vh - 14vw);
        left: 0;
        width: 10vw;
        height: 30vw;
        display:flex;
        flex-direction:column;
        justify-content:space-around;
        z-index: 3;
    }
        .chooseItem{
            text-align: center;
            color: #999999;
            font-size: 1.7vw;
            cursor: pointer;
            font-weight: bold;
            border-radius: 1vw;
            padding: 0.5vw;
            width: auto;
            transition: all 1s ease-in-out;
        }
            .chooseItem:nth-child(1){
                margin-left: 8vw;
                color: #5e5e5e;
            }
            .chooseItem:nth-child(2){
                margin-left: 14vw;
                color: #7c7c7c;
            }
            .chooseItem:nth-child(3){
                margin-left: 16vw;   
            }
            .chooseItem:nth-child(4){
                margin-left: 14vw;
                color: #7c7c7c;
            }
            .chooseItem:nth-child(5){
                margin-left: 8vw; 
                color: #5e5e5e;
            }
            .chooseSelected{
                text-shadow: -2px 0 #57068C, 0 2px #57068C, 2px 0 #57068C, 0 -2px #57068C; 
            }
        .dial{
            width: 20vw;
            height: 20vw;
            position: absolute;
            top: calc(50vh - 10vw);
            left: -10vw;
            border-radius: 20vw;
            background-color: #0d0d0d;
            border: 1vw solid #1a1a1a;
            z-index: 4;
            transition: all 1s ease-in-out;
        }
            .arrow-right {
                width: 0; 
                height: 0; 
                border-top: 1vw solid transparent;
                border-bottom: 1vw solid transparent;
                border-left: 1vw solid #1a1a1a;
                position: absolute;
                right: 1.4vw;
                top: calc(10vw - 0.7vw);
            }
        #dialBackground{
            position: absolute;
            left: -50vh;
            top: -25vh;
            z-index: 2;
            height: 150vh;
            width: 150vh;
            border-radius: 100vh;
            background-color:#262626;
            animation-name: dialBreathe;
            animation-duration: 6s;
            animation-iteration-count: infinite;
            animation-timing-function: ease-in-out;
        }

The next step for the project is to take the adapter that has been built up and connect it as a test to only the academic careers section of the code. Afterwards, there should be more work done on both the physical and the digital interfaces for the project (though I’m not sure how much fabrication l’ll be able to do over the break, so it’ll probably be mostly digital with Arduino prototyping).

Prior Art – Tangible Course Search

There are two major categories to examine when considering prior art: physical interface inspiration and  course search inspiration.

For the course search generally, I’m taking general organizational cues from Rensselaer Polytechnic Institute’s YACS student-designed course search as well as the existing NYU Class Search.

RPI YACS

For physical filter interfaces and buttons there is a more diverse field of prior artwork available. One of the most direct inspirations for the concept of a physical interface for digital search was the ITP project “Search Divides Us” made by Asha Veeraswamy, Anthony Bui, and Keerthana Pareddy. It has a higher quality physical construction than I can hope to do with my current skill set, but nonetheless the same concept is still there. I noticed it at the Maker Faire while volunteering a month ago.

Glowing Buttons

Human thought as a whole is generally non-linear, with some individuals being further towards one side of the spectrum or the other. In regards to personal experience I generally find that thought processes can run independently and start or end regardless of the current thoughts at the forefront of my mind in a fashion that is very non-linear.

The very creation of modern URLs is a concession to computational accessibility, as the whole system of registrations and lookups for domain names allow for easier and less technical access to web servers than would otherwise be required if we still used IP addresses for navigation. Furthermore, URLs allow for computational accessibility by giving access to the massive amounts of processing and connection allowed for by internet infrastructures with very little processing overhead for the end user’s system.

Technology, in its modern electrical sense, has accelerated the already rapid adoption of globalization by unifying cultures, languages, and experiences into one accessible resource and infrastructure (ex. Unicode, Internet Protocols, Programming Standardization, etc.) Technology, by its pre-electrical definition, could be argued to be a ‘cause’ of globalization insofar as the ships and trade it allowed for and required promoted exchanges of resources and ideas that might not have happened without technology as both an incentive and facilitator.  

Whether or not the last 500+ years of globalization have been ‘good’ is a topic far too nuanced for this short a response, but as a matter of personal opinion I do believe that the technological advancement and societal democratization brought about by globalization more than make up for the cultural and lingual sanitization that globalization has imparted.

Summary

For this project I created an interactive map of my travel in New York City since I’ve arrived here. It uses location data taken from my phone that can be scrubbed through using a potentiometer.

Screenshot of Webpage


Programming

For the code I set out to challenge myself and overcome the wall I hit during one of our first projects when I was not able to set up and use the NodeJS server in conjuction with Johnny-Five to receive Arduino serial communications.

Logistically, this program has a local server sync data from the potentiometer in nearly real time, which updates a variable within the looping app.js server file. Every 50 milliseconds the client-side script.js file sends an AJAX GET request back to the server at [server address]/data, to which it receives a 1-element array of the last seen potentiometer value. This format can easily be expanded to include any sort of sensor readings from the Arduino. It needs to be in either an array or object form, as integers, floats, and the like are not able to be sent by the server. Once script.js gets the data, it loops through all days since the beginning of the data adding a transparent SVG for each day with that day’s location tracking data. It also then calculates where the bottom bar should be indicated and what date that would correspond to.

Full hardware setup

//Library requests and declarations
  var express = require('express');
  var path = require('path');
  var app = express();

//Open public directory to client side access
  app.use(express.static(__dirname + '/public'));

//GET request for index
  app.get('/', function(req, res){
    //Sends 'index.html' file in response
      res.sendFile('views/index.html', {root: __dirname });
  });

//GET request for Arduino data
  app.get('/data', function(req, res){
    //Sends potentiometer data in array
    //Can't send int,float,etc. Only string, obj, and array
      res.send([potentiometer.value]);
  });

//Declare Johnny-Five board
  var five = require("johnny-five"), board, potentiometer;
//Set new board
  board = new five.Board();
//When board is ready
  board.on("ready", function() {
    // Create a new `potentiometer` hardware instance.
      potentiometer = new five.Sensor({pin: "A2",freq: 250});
  });

app.listen(3000);
var months = ["Jan","Feb","Mar","Apr","May","June","July","Aug","Sep","Oct","Nov","Dec"];
var date;
var url;
var val;

$(document).ready(function(){
  window.setInterval(function(){
      //Send GET request to local server
        $.get('http://127.0.0.1:3000/data', {}, function(data){
          //Value received in 1-element array
          //Isolate data, map it from 0 to 686 range to 0 to 49 range
          //Round data down and convert it to integer for future calculations
            val =  Math.trunc(Math.floor(data[0]*(49/686)));
        }
      //Update background color on slider bar
        $("#dayRange").css('background-image',
                    '-webkit-gradient(linear, left top, right top, '
                    + 'color-stop(' + val/49 + ', #692DAA), '
                    + 'color-stop(' + val/49 + ', #C5C5C5)'
                    + ')'
                    );
    //Reset previous days
      $(".dayTravel").remove();
    //Loop through previous days
      for(i=0;i<=val+1;i++){
        //Reset base date to start of slider-1
          date = new Date(2018, 8, 13);
        //Set date to currently calculated date
          date.setDate(date.getDate() + parseInt(i));
        //Calculate new url for image
          url = (date.getFullYear()+"").substring(2)+"-"+(date.getMonth()+1)+"-"+date.getDate();
        //Append image to body
          $("body").append("<img src='days/" + url + ".png' class='dayTravel i"+i+"' id='" + url + "'>");
      }

    //Calculate and apply slider label movement
      let revisedVal = ((val/49)*(document.body.clientWidth/1.7))+(document.body.clientWidth*(18/100));
      $(".sliderAfter").css({"left":revisedVal});
    //Calculate and apply slider label text
      let revisedText = months[date.getMonth()]+" "+date.getDate();
      $(".sliderAfter").html(revisedText);

  }, 50);//End on input change
});//End on document ready
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>P5 Homework</title>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="http://nyu.ethanprintz.com/projects/script.js"></script>

  <link rel="stylesheet" href="http://nyu.ethanprintz.com/projects/style2.css">
</head>
<body>
  <div class="sliderSide sliderBefore">Sep 14</div>
  <div class="slideContainer">
    <input class="slider" id="dayRange" type="range" min="1" max="49" value="1">
  </div>
  <div class="sliderSide sliderAfter">Sep 14</div>
</body>
</html>

After hours of debugging and documentation checking, it finally all works! It’s a bit of a simple project outwardly, but inwardly the backbone created for this one can be very easily copied and ported to future projects, which opens up more possibilities for my final project.

GIF of project working

I was originally planning to do a cool real time visualization of the Twitter API, but unfortunately I didn’t get the API keys in time. Instead I have thrown together a neat little demo animation using modulo and a for loop to create concentric circles

Link: https://editor.p5js.org/full/SJ2mTiYc7

var ellipseWidth = 1;
var ellipseRadius = 1;

function setup() {
  
  var width = 600;
  var height = 600;
  createCanvas(width, height);
  
}

function draw() {
  
  background(220);
  
	for(var i=0;i<100;i++){
    //Outer Ellipses
      fill(220);
      stroke(0);
    	ellipseRadius = (ellipseWidth%i)*4;
      ellipse((width / 2), (height / 2), ellipseRadius, ellipseRadius);
  	//Inner Ellipses
      stroke(255,0,0);
    	ellipseRadius = sin(ellipseWidth)*100;
  		ellipse((width / 2), (height / 2), ellipseRadius, ellipseRadius);
  }

  ellipseWidth++;
  
}

 

 

Symbols Post

Within the context of the reading, the long-term evolution of human communications systems was framed as a march towards eventual universality- whether deliberate or inadvertent. Each step in the march is brought upon by an invention almost solely meant for practical purposes. The ever increasing complexity of modernizing societies necessitated ever more universal systems of communication, eventually arriving at modern computational systems. This gives evidence as to the power of communication in societal development, as the two always seem to advance in lockstep. Communication allows for the expression of ideas and records necessary to increase societal complexity beyond what a singular human can remember or understand. It’s difficult to say what future communications advances might look like, but I would presume that we continue to advance towards increased universality- maybe this would mean a universal translator or increased personal integration with communications (AR, Cybernetics).

Rube Goldberg – Ethan and Sama

Ideation

As arranged with other groups, our input and outputs were both going to be servos. Because of the more simple nature of our input and output, we wanted to build something really cool for the in-between stage. After more than an hour and a half of brainstorming and ideating, we came up with the idea of doing a sailboat race inspired and powered by a blower fan, seen below in its final position, that we found on the discard shelf.

Blower Fan


Build Process

We started by figuring out what we could use as a water basin. After testing a couple different things we found on the junk shelf, we settled on my Arduino case as it had waterproof plastic ‘lanes’ in the case that we could use as channels for each boat. Next we started creating boats that could both float on the water and be properly pushed by the air. This was an especially long and laborious process, as it seemed that any design we came up with either sank, flipped over in the wind, or otherwise stopped working in some way. Eventually we came up with the boats seen below.

Boats

Concurrently with the boat development that Sama and a few helpers we found around the shop (namely Zoe) were working towards, I was working on the electronic components of the project. This included two ultrasonic sensors, LEDs, and a servo. The ultrasonic sensors were in place to determine the winner of the boat race and light up the corresponding LED before triggering the next stage of the race. The plan was originally to use one ultrasonic sensor to detect both boats, but it turned out to be quite inaccurate so we used two sensors. The final sensor and LED configuration can be seen below.

Ultrasonic Sensors

The programming was a fairly simple comparison of the two values, triggering the next stage of the Rube Goldberg if it detects one of the two boats to be close enough.

//Library Includes
#include <Servo.h>

//Pin Declaration
const int trigPin1 = 9;
const int echoPin1 = 10;
const int trigPin2 = 7;
const int echoPin2 = 8;
const int LED2 = 12;
const int LED1 = 13;
const int servoOuput = 6;
Servo servo;

//Variable Declaration

long duration;
int boat1Distance;
int boat2Distance;
bool raceOver = false;

int servoAngle = 0;

void setup() {
  //Ultrasonic Pins
    pinMode(trigPin1, OUTPUT); // Sets the trigPin1 as an Output
    pinMode(echoPin1, INPUT); // Sets the echoPin1 as an Input
    pinMode(trigPin2, OUTPUT); // Sets the trigPin1 as an Output
    pinMode(echoPin2, INPUT); // Sets the echoPin1 as an Input
  //Servo Attatchment
    servo.attach(servoOuput);
  //Open Serial Port
    Serial.begin(9600); // Starts the serial communication
  //Put servo in base position
   servo.write(0); 
}

void loop() {
    if(!raceOver){
    //BOAT ONE Distance Sensing
        // Clears the trigPin1
        digitalWrite(trigPin1, LOW);
        delayMicroseconds(2);
        // Sets the trigPin1 on HIGH state for 10 micro seconds
        digitalWrite(trigPin1, HIGH);
        delayMicroseconds(10);
        digitalWrite(trigPin1, LOW);
        // Reads the echoPin1, returns the sound wave travel time in microseconds
        duration = pulseIn(echoPin1, HIGH);
        // Calculating the distance
        boat1Distance= duration*0.034/2;
        
     //BOAT TWO Distance Sensing
        // Clears the trigPin2
        digitalWrite(trigPin2, LOW);
        delayMicroseconds(2);
        // Sets the trigPin2 on HIGH state for 10 micro seconds
        digitalWrite(trigPin2, HIGH);
        delayMicroseconds(10);
        digitalWrite(trigPin2, LOW);
        // Reads the echoPin2, returns the sound wave travel time in microseconds
        duration = pulseIn(echoPin2, HIGH);
        // Calculating the distance
        boat2Distance= duration*0.034/2;
    
      Serial.print("Boat 1 Distance: ");
      Serial.println(boat1Distance);

      Serial.print("Boat 2 Distance: ");
      Serial.println(boat2Distance);
      Serial.println("===========================");
   
      //If the race is over
        if(boat1Distance < 9 || boat2Distance < 9 ){
          raceOver = true;
          if(boat1Distance < boat2Distance){
            digitalWrite(LED1, HIGH);
            Serial.println("Boat 1 Wins!");
          }else{
            digitalWrite(LED2, HIGH);
            Serial.println("Boat 2 Wins!");
          }
        
          delay(1000);
          
          servo.write(270);              
          delay(10090);
        }
        
   }//End if race not over

}
/* Sources for Code:
   
Ultrasonic Sensor HC-SR04 and Arduino Tutorial
*/

Finally we worked the input and output into the mechanism- for input we had a block fall down triggered by an Arduino servo that completed a circuit to turn on the fan. For the output we had a servo that moved a piece of wood to hit the joystick that the next group was using as an input.


Ending

In the end however, all this work has been put in jeopardy  because the power supply unit that we were using suddenly decided that the fan wanted 26 volts of DC power instead of the 12 volts that it was happily providing for the seven hours beforehand. As far as I can tell, it shorted the internal circuitry of the fan just as we were doing our final tests of the night. We have a few different options for where to go from here. One is to buy a new fan ($8-15 on Amazon) and have it shipped here before Wednesday. Another is to find two smaller fans that we could possibly incorporate.  Or we can use one large, constantly blowing fan and two servos to hold the boats back from going before they circuit has been completed.


Credits

Zoe, Josh, Andri, Katie, Ruyi, and some ITP people for their help throughout the process.

One’s body is crucial to their understanding of both the environment around them and to the larger world. Its physical confines and senses serve as our window to the world around us, undoubtedly influencing our processing of said spaces. Embodiment provides a framework for this concept.

As our understanding of the world happens through physical space and physical interaction, a computer that wishes to attach itself to physical cognition most also operate within this space. Beyond simply allowing the user to type on a keyboard, computers that offer a greater variety of input types can better take advantage of embodiment. For example, Dan Oved’s project at Maker Faire New York that used his created PoseNet algorithm to move a physical sculpture [1] could instead use subtle changes in posture observed by researches as an integrated emotional input mechanism. [2]


[1] https://www.danioved.com/portfolio/posenet/
[2] http://journals.sagepub.com/doi/abs/10.1177/0956797609359333

 

Machines represent an interesting dichotomy in their effect on moral decision making. On one hand, computers can represent an ‘ideal’ morally neutral machine- they can be unaffected by societal biases and untainted by hostility and short-sightedness. On the other hand this neutrality can extend into an amoral state if left on their own, prioritizing whatever the programmer instructed without regard to moral consequences. I believe that technology, especially in the age of the internet, has generally assumed the latter role. In the age of algorithms and machine learning, it is widely apparent that largest and most important functions in our society have been delegated to algorithms that seek out objectives without consideration to morality, whether that be maximizing advertisements clicked, videos watched, or monetary value gained.

Window-Based Smart Home Trigger (With NodeJS/Johnny-Five Integration)

Introduction

This week I wanted to turn the “theoretical hook-in to a smart home system” that I referenced in the conclusion for my last assignment into more of a concrete reality. This included slightly rewiring the breadboard and fully reprogramming the assignment in Javascript. I still haven’t made it all the way to this goal, but this project is a large step in the direction. The three systems used for this assignment were Arduino, NodeJS, and Johnny-Five.

Logos Used


Hardware

The wiring of the previously used circuit was slightly changed to accommodate the required digital output pin in addition to the original digital input pin.  The LED output was added to digital pin 3 and the input continues to be on digital pin 5. The wiring diagram can be found below. The switch continues to be based on copper tape lining a window that is opened or closed.

Arduino Wiring Diagram


Software

Many of the changes for this iteration lie in the software. Instead of using a simple serial log and a hand-wavy gesture towards a ‘smart home system’, this is a more advanced method.

First, the setup process for the system. I started by uploading the ‘StandardFirmataPlus’ code found under File>Examples>Firmata in the Arduino IDE. By uploading this code to the Arduino, I can communicate with the hardware in real time over the serial port rather than uploading C code through the IDE. I then installed the Johnny-Five libraries over command line npm (this step requires NodeJS to already be installed).

npm i johnny-five express socket.io -g --save

Next is the actual programming of the system. The original test program I created shown below was a trial to make sure the system works with the pre-existing wiring and pins. This program blinks the LED when the window is closed.

var five = require("johnny-five");
var board = new five.Board();

board.on("ready", function() {
  //Pin Declaration
    var led = new five.Led(3);
    var button = new five.Button(5);

  //Main Program
    button.on("down", function() {
      console.log("down");
      led.blink();
    });

});

This code can be directly copied into the app.js file of a NodeJS server, which when combined with a socket.io instance allows direct communication between the server and the hardware. At this point the project is about 2/3 of the way to full internet of things integration.

 

Window-based Smart Home Trigger

For this project I wanted to explore the creation of Smart Home/IoT devices. The instance I chose was the detection of a window opening and closing, and the relay of that state to a computer. The original plan called for a servo-based actuation of the A/C power button as well, but I did not have enough time to design a mount for the mechanism and it fell outside of the scope of the project anyway.


Arduino Diagram

The switch mechanism itself is triggered off of whether the window  is in a closed or open state. There are two pieces of copper tape with soldered wires, one on the moving window pane and the other a the base of the window frame. When the two pieces are separated, the upper portion of the circuit as indicated by the diagram above is left open and the digital input port reads LOW  as its current state. When the contacts are touching and the circuit is closed, this reading switches to HIGH . This circuit is based off of the demo ‘State Change Detection’ configuration as found on the official Arduino website. [1]

Arduino Window GIF

While my hands were used in the creation of this GIF, it is not necessary to use hands in the operation of the window and I often just use an elbow or arm to close it. The usage of the hands here was so I could reach over the camera without hitting it.

The theoretical hook-in to a smart home system is communicated over the serial port of the Arduino unit. This serial communication can be, for example, picked up by a NodeJS-based system running off of a local server. That is just theoretical though, as the full extent of this project for now was just to send the window close/open flag over the serial port.


[1]  https://www.arduino.cc/en/Tutorial/StateChangeDetection