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

// 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
//Set app to port 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">
    <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>
    <!--<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 id="chooseDial" class="dial"><div class="arrow-right"></div></div>
//On Document Ready

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

    //Send AJAX call to server every 20ms
        setInterval(function() {
                    url : 'http://localhost:8080/data',
                    type : 'GET',
                    // 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){
                        $("#chooseDial").css({'transform' : 'rotate(-55deg)'});
                // 2| 206 to 411
                    else if(potentiometerValue>=206 && potentiometerValue<411){
                        $("#chooseDial").css({'transform' : 'rotate(-35deg)'});
                // 3| 412 to 617
                    else if(potentiometerValue>=412 && potentiometerValue<617){
                        $("#chooseDial").css({'transform' : 'rotate(0deg)'});
                // 4| 618 to 823
                    else if(potentiometerValue>=618 && potentiometerValue<823){
                        $("#chooseDial").css({'transform' : 'rotate(35deg)'});
                // 5| 824 to 1023
                    else if(potentiometerValue>=824  && potentiometerValue<1023){
                        $("#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 */
    margin: 0;
    padding: 0;
    background-color: #1f1d1d;
    font-family: sans-serif;
    width: 100vw;
    height: 100vh;
    overflow: hidden;
        color: #999999;
        font-size: 2vw;
        position: absolute;
        top: 1vw;
        left: 1vw;
        font-weight: bold;
        position: absolute;
        top: calc(50vh - 14vw);
        left: 0;
        width: 10vw;
        height: 30vw;
        z-index: 3;
            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;
                margin-left: 8vw;
                color: #5e5e5e;
                margin-left: 14vw;
                color: #7c7c7c;
                margin-left: 16vw;   
                margin-left: 14vw;
                color: #7c7c7c;
                margin-left: 8vw; 
                color: #5e5e5e;
                text-shadow: -2px 0 #57068C, 0 2px #57068C, 2px 0 #57068C, 0 -2px #57068C; 
            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);
            position: absolute;
            left: -50vh;
            top: -25vh;
            z-index: 2;
            height: 150vh;
            width: 150vh;
            border-radius: 100vh;
            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).

3 thoughts on “Tangible Course Search – Proof of Concept”

  1. Sorry to see that your project wasn’t quite working today, but I can’t wait to see it all done! I didn’t really understand where it went wrong, so sorry I can’t provide more feedback on your project! But you can do it! I think it’ll all work out ! Good job!

  2. I love your interface design; however, I am curious on why you chose a design that is so different from the standard NYU aesthetic (white with purple detailing)

Leave a Reply