Posted on and Updated on

Design for Discomfort / Journey 2

http://andrewmccausland.net/designForDiscomfort/attention/index

1. Choose a form of discomfort (choose one of the four forms, and get more specific from there)

Visceral— sensory overload (fast-paced flashing lights and colors, visual obstruction, inability to concentrate)

2. Identify a goal that could be reached through this discomfort

Get participants to think about their attention spans and how they are captured and manipulated by external stimuli. Ideally would like to facilitate dialogue surrounding this topic to span more overarching issues regarding our visual landscape (for example the advertising industry, technology’s effects on our mental abilities).

3. Identify a design approach (must be different than Journey #1) that can utilize your chosen form of discomfort.

A web-based experience (the space which is most susceptible to such discomfort).

4. Prototype a “journey” using that approach, toward the goal, through the chosen form of discomfort.

A web-based experience where users are presented with the objective of reading an essay, but are subsequently confronted with increasing sensory distraction inhibiting their ability to finish it (they wouldn’t have read it in its entirety anyway, right?)


This prototype focuses on testing the following factors:

  • User interest/engagement (How far will they get? If not far, why not? Is it boring? Does it get too uncomfortable too quickly?)
  • Does the user understand the purpose of the experience?
  • Did the user find the experience worthwhile/rewarding? Does it actually facilitate dialogue/thought as per my design goals, or in any other way?

Based on the results for these research points, I’d like to move on to figuring out what contexts the experience(s) should be presented in (how will users stumble upon this?) then build more complex web-based experiences utilizing similar subtle, encroaching a/v annoyances.

Posted on and Updated on

Live Web ⁄ Class 2 ⁄ Node + Socket.io Chat

Live url TBA, DigitalOcean locked my account immediately after registration and haven’t got back to me yet.

html & css:

<html>
	<head>
		<title>CH4T</title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
		<script type="text/javascript" src="/socket.io/socket.io.js"></script>
		<script type="text/javascript" src="chat.js"></script>
		<script type="text/javascript">			
		</script>
	</head>
 <body id="body">
	 <div id=main>
		 <input type="text" id="message" name="message">
		 <input type="submit" value=">" onclick="sendmessage(document.getElementById('message').value);">
		 <div id="messages"></div>
		 </div>

		 <style>
			 #main {
			 	/*margin:50px;*/
			 }
			 .newBg{
			 	position:absolute;
			 	top:0;
			 	right:0;
			 	z-index: -1;
			 	width:100vw;
			 	height:90vh;
			 }
			 input {
			 	/*width:100%;*/
			 	border-radius: 2px;
			 }
			 button {
			 	border-radius: 2px;
			 }
			.msgBox {
				position:relative;
				float:left;
				display:block;
				width:100%;
				padding:5px;
				margin:5px 10px 0 0; 
				background:rgba(0,0,0,0.05);
				border-radius: 2px;
			}
		</style>
 </body>
</html>

js:

var socket = io.connect();

socket.on('connect', function() {
    console.log("Connected");
});

// Receive from any event
socket.on('chatmessage', function(data) {
    console.log(data);

    //colors
    var colors = ["red", "orange", "yellow", "green", "blue", "purple", "pink", "brown", "black", "white"];
    for (var i = 0; i < colors.length; i++) {
        if (data == colors[i]) {
            var body = document.getElementById("body");
            body.style.background = colors[i];
        }
    }

    //bg images
    if (data == "zoom") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/zoom.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "blood") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/blood.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "cat") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/cat.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "explode") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/explode.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "kirby") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/kirby.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "pizza") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/pizza.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "werq") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/professional.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "rain") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/rain.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "snow") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/snow.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "sparkle") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/sparkle.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "water") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/water.gif')";
        document.body.appendChild(newBg);
    }
    if (data == "wizard") {
        // document.body.style.backgroundImage = "url('img/zoom.gif')";
        var newBg = document.createElement("newBg");
        newBg.className = "newBg";
        newBg.style.backgroundImage = "url('img/wizard.gif')";
        document.body.appendChild(newBg);
    }

    if (data == "nothing") {
        var thisBg = document.getElementsByClassName("newBg");
        document.body.removeChild(thisBg);
    }

    var msgBox = document.createElement("msgBox");
    msgBox.className = "msgBox";
    msgBox.innerHTML = data;

    document.getElementById("messages").appendChild(msgBox);
});

var sendmessage = function(message) {
    console.log("chatmessage: " + message);
    socket.emit('chatmessage', message);
};

Posted on and Updated on

Design for Discomfort ⁄ Daily Practice

When considering what I could do as my daily practice, I thought about the ways I’d like to grow over the next several weeks, and the biggest thing that came to mind was the amount of time I spend in front of the screen. I decided to draw for 30 minutes each day. Turning off my monitors and focusing on the page for this duration would be an exercise in leaving my comfort zone; I would have to fight impulses to check social media, surf the web, etc. Then, I would have to document my progress forcing me to publish/share my drawings.

[Documentation feed is here]

Note: I started on Saturday. I had trouble coming up with a feasible plan until then.

Posted on

Live Web ⁄ Class 1 ⁄ Self Portrait

[Live url here]

My self portrait consists of a series of videos, which together compose an image of my head, that collectively follow the cursor as it moves left and right across the window. In order to achieve this, I first recorded a video of myself turning from one profile position to the other. The video lasts about 25 seconds. After recording I split the video vertically into 10 even pieces and added them to the DOM as 10 separate <video> elements. Then I used js to map the length of the video in seconds to the width of the window in pixels, and had the video fast-forward or rewind to the time that corresponds to the current mouse position, every time mouse movement is detected.

I split the portrait into several pieces so that I could play around with the variables that determine the number of frames and the time each video transition would take to fast-forward/rewind from one point to another. I like the resulting jumpy effect and the way my face gets abstracted at certain moments and then slowly pieces back together.

The html/css…

<html>
	<head>
		<script type="text/javascript" src="main.js"></script>
	</head>
	<body>
	<br>
	<br>
	<div id="2">
		<video id="vid11" src="vid/Comp7_1.mp4" width="9%" height="100%" loop></video>
		<video id="vid12" src="vid/Comp7_2.mp4" width="9%" height="100%" loop></video>
		<video id="vid13" src="vid/Comp7_3.mp4" width="9%" height="100%" loop></video>
		<video id="vid14" src="vid/Comp7_4.mp4" width="9%" height="100%" loop></video>
		<video id="vid15" src="vid/Comp7_5.mp4" width="9%" height="100%" loop></video>
		<video id="vid16" src="vid/Comp7_6.mp4" width="9%" height="100%" loop></video>
		<video id="vid17" src="vid/Comp7_7.mp4" width="9%" height="100%" loop></video>
		<video id="vid18" src="vid/Comp7_8.mp4" width="9%" height="100%" loop></video>
		<video id="vid19" src="vid/Comp7_9.mp4" width="9%" height="100%" loop></video>
		<video id="vid20" src="vid/Comp7_10.mp4" width="9%" height="100%" loop></video>
	</div>
	</body>
	<style>
		video {
		object-fit: fill;
		}
		#2 video{
			display:inline;
			position:relative;
			float:left;
		}
		#2{
			height:800px;
		}
	</style>
</html>

…and the JS.

//src:
//[1] https://stackoverflow.com/a/7790764 - capturing mouse pos
//[2] https://stackoverflow.com/a/10756409 - range conversion
//[3] https://stackoverflow.com/a/36731430 - FF/RW video to given time

function init() { //all js that needs to happen after page has loaded
    document.onmousemove = handleMouseMove; //[1]

    function handleMouseMove(event) {
        var dot, eventDoc, doc, body, pageX, pageY;

        event = event || window.event; //IE

        // If pageX/Y aren't available and clientX/Y are,
        // calculate pageX/Y - logic taken from jQuery.
        // (For old IE)
        if (event.pageX == null && event.clientX != null) {
            eventDoc = (event.target && event.target.ownerDocument) || document;
            doc = eventDoc.documentElement;
            body = eventDoc.body;

            event.pageX = event.clientX +
                (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
                (doc && doc.clientLeft || body && body.clientLeft || 0);
            event.pageY = event.clientY +
                (doc && doc.scrollTop || body && body.scrollTop || 0) -
                (doc && doc.clientTop || body && body.clientTop || 0);
        }

        // console.log(event.pageX);
        var width, vid_length_s, vid_timeToSkipTo;
        // width = screen.width;
        width = window.innerWidth;
        vid_length = 25;
        vid_timeToSkipTo = convertToRange(event.pageX, [0, width], [0, vid_length]);
        console.log(vid_timeToSkipTo);
        goToTime(Math.floor(vid_timeToSkipTo));
    }
}

window.addEventListener('load', init);

function convertToRange(value, srcRange, dstRange) { //[2]
    // value is outside source range return
    if (value < srcRange[0] || value > srcRange[1]) {
        return NaN;
    }
    var srcMax = srcRange[1] - srcRange[0],
        dstMax = dstRange[1] - dstRange[0],
        adjValue = value - srcRange[0];

    return (adjValue * dstMax / srcMax) + dstRange[0];

}

function goToTime(time) { //[3]
    var vid11 = document.getElementById('vid11'),
        ticks11 = 10, // number of frames during fast-forward
        frms11 = 100, // number of milliseconds between frames in fast-forward/rewind
        endtime11 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta11 = (endtime11 - vid11.currentTime) / ticks11;
    var startTime11 = vid11.currentTime;
    for (let i = 0; i < ticks11; ++i) {
        (function(j) {
            setTimeout(function() {
                vid11.currentTime = startTime11 + tdelta11 * j;
            }, j * frms11);
        })(i);
    }
    var vid12 = document.getElementById('vid12'),
        ticks12 = 10, // number of frames during fast-forward
        frms12 = 150, // number of milliseconds between frames in fast-forward/rewind
        endtime12 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta12 = (endtime12 - vid12.currentTime) / ticks12;
    var startTime12 = vid12.currentTime;
    for (let i = 0; i < ticks12; ++i) {
        (function(j) {
            setTimeout(function() {
                vid12.currentTime = startTime12 + tdelta12 * j;
            }, j * frms12);
        })(i);
    }
    var vid13 = document.getElementById('vid13'),
        ticks13 = 10, // number of frames during fast-forward
        frms13 = 200, // number of milliseconds between frames in fast-forward/rewind
        endtime13 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta13 = (endtime13 - vid13.currentTime) / ticks13;
    var startTime13 = vid13.currentTime;
    for (let i = 0; i < ticks13; ++i) {
        (function(j) {
            setTimeout(function() {
                vid13.currentTime = startTime13 + tdelta13 * j;
            }, j * frms13);
        })(i);
    }
    var vid14 = document.getElementById('vid14'),
        ticks14 = 10, // number of frames during fast-forward
        frms14 = 250, // number of milliseconds between frames in fast-forward/rewind
        endtime14 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta14 = (endtime14 - vid14.currentTime) / ticks14;
    var startTime14 = vid14.currentTime;
    for (let i = 0; i < ticks14; ++i) {
        (function(j) {
            setTimeout(function() {
                vid14.currentTime = startTime14 + tdelta14 * j;
            }, j * frms14);
        })(i);
    }
    var vid15 = document.getElementById('vid15'),
        ticks15 = 10, // number of frames during fast-forward
        frms15 = 300, // number of milliseconds between frames in fast-forward/rewind
        endtime15 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta15 = (endtime15 - vid15.currentTime) / ticks15;
    var startTime15 = vid15.currentTime;
    for (let i = 0; i < ticks15; ++i) {
        (function(j) {
            setTimeout(function() {
                vid15.currentTime = startTime15 + tdelta15 * j;
            }, j * frms15);
        })(i);
    }
    var vid16 = document.getElementById('vid16'),
        ticks16 = 10, // number of frames during fast-forward
        frms16 = 350, // number of milliseconds between frames in fast-forward/rewind
        endtime16 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta16 = (endtime16 - vid16.currentTime) / ticks16;
    var startTime16 = vid16.currentTime;
    for (let i = 0; i < ticks16; ++i) {
        (function(j) {
            setTimeout(function() {
                vid16.currentTime = startTime16 + tdelta16 * j;
            }, j * frms16);
        })(i);
    }
    var vid17 = document.getElementById('vid17'),
        ticks17 = 10, // number of frames during fast-forward
        frms17 = 300, // number of milliseconds between frames in fast-forward/rewind
        endtime17 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta17 = (endtime17 - vid17.currentTime) / ticks17;
    var startTime17 = vid17.currentTime;
    for (let i = 0; i < ticks17; ++i) {
        (function(j) {
            setTimeout(function() {
                vid17.currentTime = startTime17 + tdelta17 * j;
            }, j * frms17);
        })(i);
    }
    var vid18 = document.getElementById('vid18'),
        ticks18 = 10, // number of frames during fast-forward
        frms18 = 250, // number of milliseconds between frames in fast-forward/rewind
        endtime18 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta18 = (endtime18 - vid18.currentTime) / ticks18;
    var startTime18 = vid18.currentTime;
    for (let i = 0; i < ticks18; ++i) {
        (function(j) {
            setTimeout(function() {
                vid18.currentTime = startTime18 + tdelta18 * j;
            }, j * frms18);
        })(i);
    }
    var vid19 = document.getElementById('vid19'),
        ticks19 = 10, // number of frames during fast-forward
        frms19 = 200, // number of milliseconds between frames in fast-forward/rewind
        endtime19 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta19 = (endtime19 - vid19.currentTime) / ticks19;
    var startTime19 = vid19.currentTime;
    for (let i = 0; i < ticks19; ++i) {
        (function(j) {
            setTimeout(function() {
                vid19.currentTime = startTime19 + tdelta19 * j;
            }, j * frms19);
        })(i);
    }
    var vid20 = document.getElementById('vid20'),
        ticks20 = 10, // number of frames during fast-forward
        frms20 = 150, // number of milliseconds between frames in fast-forward/rewind
        endtime20 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta20 = (endtime20 - vid20.currentTime) / ticks20;
    var startTime20 = vid20.currentTime;
    for (let i = 0; i < ticks20; ++i) {
        (function(j) {
            setTimeout(function() {
                vid20.currentTime = startTime20 + tdelta20 * j;
            }, j * frms20);
        })(i);
    }
}

The code is pretty sloppy and could probably be optimized a lot if given more time. The videos perform horribly on Firefox and Chrome, while working fine in Safari. In general, playing 10 videos at once obviously causes average-performing computers to increase in temperature at an unacceptable rate and would probably eventually cause the browser to crash. Instead, there is probably a way for one video file to be used that can be duplicated and cropped as necessary.

Posted on and Updated on

Understanding Networks ⁄ Class 1 ⁄ Ball Drop Game

The controller I built consists of a two-axis joystick (`l`,`r`,`u`,`d`) and two buttons (one for `x`,and one that prints `in=andrew\n` on the first press, and just `i` on every subsequent press after that). I was unable to implement a Wifi101 or Ethernet shield, so the controller must be plugged in to function. All of this fit nicely into an iPhone case:

A video of it working:

The shell command is something like the following but with the appropriate usb input name and ip address:

cat /dev/cu.usbmodem1421 | nc 172.16.234.202 8080

Posted on and Updated on

Live Web ⁄ Class 1 ⁄ Analyzing a Live Online Platform: Second Life

Second Life is a massively multiplayer online role-playing sandbox game. Whether or not it’s to be considered a game is questionable as many users seem to take it as seriously as they would their normal lives, even going so far as to make and spend real money for virtual goods and services. One of the defining feature of SL is that it’s a 3d-rendered environment that functions in real-time, similarly to many other contemporary MMOs.

SL provides real-time visual and sound-related interactions with the environment and between users. A user can have their avatar perform actions with objects (sit in chairs, open doors, etc), and perform actions with other users (chat via text or microphone, have their avatars perform actions together, swap items, teleport to each other, etc).

But what’s most interesting about SL is the way it connects various parts of the WWW to its environments. Owners of a specific piece of land (a “parcel”) on a server (a “sim”) can stream music in the format of a radio station into the simulation for all visitors to hear. Some sims are designated shopping centers containing walls of objects that can be clicked on to purchase and download goods and services from https://marketplace.secondlife.com.

Seemingly any kind of web media can be embedded into the environment:

How does SL use live data streams?

http://wiki.secondlife.com/wiki/Live_Data_Feeds#Statistics

http://wiki.secondlife.com/wiki/Streaming_Video_in_Second_Life