My Technical Skills

Skills I’m the most deeply Invested in currently

These are the languages and technologies I have the most experience with and have used in a number of different projects.

Web languages:

  • HTML5, CSS, Javascript, JQuery, Ajax
  • PHP, ASP/ASP.NET
  • MySQL, SQL Server, Oracle
  • XML
  • Flash, Actionscript
  • Facebook API, Twitter API
  • Photoshop, Fireworks, Dreamweaver, Illustrator

I have been experimenting in mobile for Android as well:

  • Java, XML, Eclipse

(None of these technologies are individually new to me though)

I also have experience developing for

I have an extensive background with traditional desktop programming from various projects (under PC and Unix environments) as well. I am less interested in developing for these environments, though, in the future.

Desktop Programming:

  • C/C++ (Undergraduate), Java (various), J# (Guardians of Kelthas), C# (Sony)
  • Visual Studio, Eclipse
  • DirectX (Guardians of Kelthas), OpenGL (Undergraduate)
  • .NET Framework (Sony & Guardians of Kelthas), MFC (experimenting), Windows Sockets (experimenting)

I am always interested in learning new technologies and languages!

Twitterspace

Context

  • Indiana University (Bloomington, IN)
  • Ph.D. project (co-creator)

Case

Requirements: I was tasked with creating the front end in Haxe and Actionscript for a prototype to take a tweets of the those a Twitter account was following and display them animating across the screen.

This is the draw function in the Tweet class, responsible for creating the individual tweets.
public function draw() {
        if (user_screen_name == "wnryan") {
			boxColor = 0x430000;
        }

	//a movie clip to store the background of the tweet
	var base:MovieClip = new MovieClip();

	//draw the elipse to form the background and call it background box
	base.graphics.beginFill(boxColor);
	base.graphics.lineStyle(1, borderColor);
	base.graphics.drawRoundRect(boxX, boxY, boxWidth, boxHeight, 25, 25); //fix this ellipse
	base.graphics.endFill();
	base.name = "background box";
	addChild(base);  //add it to this tweet (which is a movie clip itself)

	//add a text format
	var format:TextFormat = new TextFormat();
	format.font = "Arial";
	format.color = textColor;
	format.size = textSize;

	//setup the text properly and give it a name tweet
	tweetText = new TextField();
	tweetText.selectable = false;
	tweetText.embedFonts = true;
	tweetText.wordWrap = true;
	tweetText.antiAliasType = "advanced";
	tweetText.type = "dynamic";
	tweetText.name = "tweet";

	tweetText.defaultTextFormat = format;
	tweetText.htmlText = message; //assign what the tweet is

	//position the text and give it a box
	tweetText.x = boxX + textX;
	tweetText.width = textWidth;
	tweetText.height = textHeight;

	addChild(tweetText);   //add it to this tweet (which is a movie clip itself)

	//load the image for this user externally and name this user image
	var ldImage : Loader = new Loader();
	var url:String = profile_image_url;

	try {
		var urlReq:URLRequest = new URLRequest(url);
		ldImage.load(urlReq);
	} catch (error:String) {
	    	trace("Unable to load URL: "+url);
                trace("Error: "+error);
        }
	ldImage.name = "user image";

	//position and size this image
	ldImage.x = boxX + imageX;
	ldImage.y = boxY + imageY;
	ldImage.scaleX = 0.6;//imageHeight / ldImage.content.width;
	ldImage.scaleY = 0.6;//imageWidth / ldImage.content.height;

	addChild(ldImage); //add it to this tweet (which is a movie clip itself)
}
This is the onEnterFrame for the Main class, which is responsible for animating the Tweets and also updating any tweets already on the screen.
public function onEventFrame(evt:Event) :Void {

   for (i in 0...tweets.length) {
       //go through all the tweets
       if (tweets[i].direction) {
            //if the direction is true, move them to the right
            if (tweets[i].x > scrWidth) {
                //if they are at the end of the screen, place them before the front of the screen
                tweets[i].x = ( - 1 * tweets[i].boxWidth);
            } else {
                tweets[i].x += Math.ceil(tweets[i].movementRate);
            }
        } else {
            //if the direction is false, move them to the left
            if (tweets[i].x < ( - 1 * tweets[i].boxWidth)) {
                //if they are at the front of the screen, place them after the end of the screen
                tweets[i].x = scrWidth;
            } else {
                tweets[i].x -= Math.ceil(tweets[i].movementRate);
            }
        }
     }
     if (loadingNewTweet) {
        // if we had a new tweet popup
        if (newBox.alpha < 0.8) {
            // fade in our tweet to 80% alpha
            newBox.alpha = newBox.alpha + 0.05;
        } else {
            loadingNewTweet = false;
        }
     }
     if (unloadingNewTweet) {
         // if we are getting ride of the popup
         if (newTweet.hitTestPoint(newTweetCurX + 1, newTweetCurY + 1) || newBox.alpha <= 0) {
            //if we hit the pop up on the tweet that it is or it is no longer visible
            unloadingNewTweet = false;
            removeNewBox();
            // remove the pop up
         } else {
      	    newBox.alpha = newBox.alpha - 0.04;
            //gradually fade out the box
            //this was arrived at through trial and error and looks reasonably good, there is a difference in the jumps along x and y
            //because this will generally need to travel farther on the x coordinate than y
            newBox.x += ((newTweet.x + newTweet.width / 2) - newTweetCurX) / 10 + (0.1 * newBox.width);
            // jump 1/10 the distance to the tweet and also take into account what was lost through scaling
            newTweetCurX += ((newTweet.x + newTweet.width / 2) - newTweetCurX) / 10;
            //move the representation for the center point by this amount (with out the scaling
            newBox.y += ((newTweet.y + newTweet.height / 2) - newTweetCurY) / 5 + (0.1 * newBox.height);
            // jump 1/5 the distance to the tweet and also take into account what was lost through scaling
            newTweetCurY += ((newTweet.y + newTweet.height / 2) - newTweetCurY) / 5;
            //move the representation for the center point by this amount (with out the scaling
            //gradually shrink the box
            newBox.scaleX = newBox.scaleX * 0.9;
            newBox.scaleY = newBox.scaleY * 0.9;
      	}
   }
}

Result: Using the Twitter API, we created an external database of community Tweets. The front end took that data, pulled user information, and animated it using the Haxe interface via Actionscript.

twitterspace_popup

This is an example image of the working system. The real system is no longer pulling data from Twitter due to changes in the API.

Click here to see an example with sample data.

Outcomes: The Twitterspace display was deployed for our school as well as 3 academic conferences and 3 other organizations.

Details

Dates of Project: Jan 2007-Dec 2010

Development Skills Used: Haxe, Actionscript, Audacity

My Roles: Research, Design, Writing, Programming, Artistry

Device Ecology Mapper

Context

  • Indiana University (Bloomington, IN)
  • Ph.D. project (primary developer)

Case

Requirements: I created the front end of a system in Flash for recording data related to devices users own as well as the back end that recorded data using PHP and MySQL from the prototype.

database

This is the database structure used for storing participant data.

This is the code for taking data inputted by participant and sending it to the PHP file to process and load into a database.
function submitData() {
	//find how long it took for the participant to complete the map
	var dt_date = new Date();
    	completion_time = dt_date.getTime();
	var dif_time = completion_time - start_time;

        //when submitting data
	var user_str = userid+"|"+base+"|"+dif_time+"|"+deletions+"|"+submitted;
	var art_str = "";
	var conn_str = "";
	var anno_str = "";

        //work through array of devices
	for(iter=0;iter<map_array.length;iter++) {

		//separate device string by parsable @ symbol
		if(iter > 0) art_str += "@";

                //include device number, url, name, and x, y position
		art_str += iter + "," + artsUsed_array[iter][0] + "," + artsUsed_array[iter][1] + ",";
	    art_str += map_array[iter]._x + "," + map_array[iter]._y;
	}

        //work through array of connections
	for(iter=0;iter<conn_array.length;iter++) {

		//separate connection string by parsable @ symbol
		if(iter > 0) conn_str += "@";

                //include connection number, devices connected, and type
		conn_str += iter + "," + conn_array[iter][0] + "," + conn_array[iter][1] + "," + conn_array[iter][2];
	}

        //work through array of annotations
	for(iter=0;iter<anno_array.length;iter++) {

	    //separate annotation string by parsable @ symbol
	    if(iter > 0) anno_str += "@";

		//include what annotation is connected to and device text
		anno_str += anno_array[iter][0] + "," + anno_array[iter][1] + "," + anno_array[iter][2]["text"].text;
	}

        //combine sections together separate by parsable % symbol
	var send_str = user_str + "%" + art_str + "%" + conn_str + "%" + anno_str;

        //Display message for success or failure
	var resultUpdate_lv = new LoadVars();
  	resultUpdate_lv.onLoad = function(success) {
	  	dyn_txt.text = "Load 2";
	  	if(success) {
   	  		dyn_txt.text = "Map data saved.";
	  	} else {
			dyn_txt.text = "Data save error.";
	  	}
	};

        //load php file with data
	var loadUpdate_lv = new LoadVars();
  	loadUpdate_lv.sendAndLoad(base+"saver.php?type=update&data="+send_str, resultUpdate_lv, "POST");
}
This is the code for adding a new device to the “canvas” containing devices owned or used by the user.
function createArtifact(cat, subcat, art) {
        //increase symbol depth
	var depth = map_array.length;

        //add device to canvas at depth of new symbol + 600 depth
	//this makes sure it is on top
	var image = _root.createEmptyMovieClip("artifact"+depth+"_mc", 600+depth);
	image._x = deviceMap_mc._x-0.5*deviceMap_mc._width+575;
	image._y = deviceMap_mc._y-0.5*deviceMap_mc._height+365;
	var name = "";
	var url = "";

        //if create new device is selected, just put a filler image there
        if (cat == -1 & subcat == -1 && art == -1) {
		image.loadMovie(base+"icons/defaultimage.jpg");
		name = "Default";
		url = base + "icons/defaultimage.jpg";

 	//if section did not have subcategory load movie
	} else if (subcat == -1) {
		image.loadMovie(base + cat_array[cat][art][1]);
		name = cat_array[cat][art][0];
		url = cat_array[cat][art][1];

	//if it did have subcategory load movie
	} else {
		image.loadMovie(base + cat_array[cat][subcat][art][1]);
		name = cat_array[cat][subcat][art][0];
		url = cat_array[cat][subcat][art][1];
	}

	//add device to device array
	map_array.push(image);
	artsUsed_array.push(new Array(name, url));
	callbackSetting_array.push(false);

	//activate object selection tool
	tObj_mc.gotoAndStop(2);
	tConn_mc.gotoAndStop(1);
	tAnno_mc.gotoAndStop(1);
	tDel_mc.gotoAndStop(1);
}

Result:

  • The prototype allowed for users to drag and drop from a categorized list of devices.
  • Devices loaded in externally
  • Allowed users to create links between devices.
  • Recorded overall statistics of use and connection data.

device

This is a shot of the digital prototype. A version that does not record data still is available.

Click here to make your own device ecology map on the digital prototype.

Outcomes: The data recording portion of the prototype was completed. It never was deployed, but it is ready for that purpose.

Details

Dates of Project: Jan 2008-Mar 2012

UX Methods Used: Flash, Actionscript, MySQL, PHP, XML

My Roles: Research, Writing, Artistry, Programming, Design

Guardians of Kelthas

Context

  • Indiana University (Bloomington, IN)
  • Masters project (co-creator)

Case

Requirements: I was tasked with developing the interface for the Guardians of Kelthas, a deck building game, in J# using DirectX. I took the artwork from our team of artists and presented it to the user to help them install, setup the game, and play the game.

Result: We created an interface, allowing us to seamlessly transition between the interface and the game playing environments.

  • I created all game setup and creation screens.
  • I also constructed the game playing interface itself aside for the cards.
  • Finally, I programmed an installer for the game using the NSIS scripting language.

kelthas

This is an image of the gameplay interface.

kelthas2

This is an image of the deck management system created in part with the game for campaigns and online games.

video

This is a promotional video clip that was created for the game. The video shows the game’s opening animation and several of the game interfaces, including the main interface.  Unfortunately, the game itself is no longer supported by Microsoft.

Outcomes: The game was sold as part of a package of independent games at a website called the Great Games Experiment, which is now defunct. We also submitted the game to a number of game competitions:

  • IDEASFEST 2005, 2006: Best Game, People’s Choice Awards
  • FuturePlay 2005: Competition Finalist
  • Slamdance 2006: Gamemaker Competition Finalist

 Details

Dates: Sept 2004-Apr 2006

Development Skills: J#, .NET Framework, Direct X, NSIS scripting, Visual Studio

My Roles: Interface Design, Programming

Kaizen Tracking System

Context

  • Sony Corporation of America (Mt. Pleasant, PA)
  • Production Systems Intern
Sony website

Case

Requirements:

  • Create a system to centralize Kaizen reporting in one unified system.
  • Create a back-end to monitor, search, and report on Kaizen’s across the complex.
  • Find a way to fit this system into user’s habits of Kaizen reporting.

Result:The final system utilized a standard framework across our division through ASP.NET, C#.NET, and SQL Server.

Outcome:Initially after system was deployed, users maintained their old ways of using PowerPoint to create Kaizen reports. With additional training and reminding, the system was able to be worked into the user’s workflow.

Details

Dates: May 2002-Aug 2002

Development Skills: ASP.NET, C#.NET, SQL Server, User Acceptance Testing, Training

My Roles: Programming, Design, Research

Ascenvia

Context

  • Ascenvia (Atlanta, GA)
  • Vice President, head programmer
head02

Case

Requirements:

  • Designing a web resource/community for developers in PHP/MySQL.
  • Becoming a portal for tutorials available on other sites and connect with other developers
  • We were also looking to find employers who would want to hire people via the site.

jobs

This is a database schema for the jobs section on the site. While this structure was implemented, it was never fully-rolled out due to a lack of resources; it would have been a precursor to a tech-focused LinkedIn.

tutorials

This is the database structure for the tutorial engine. Note: The hold and tutorials had similar structures, but the hold provided a place where we could review tutorials before adding them to the website.

This is an example of a tutorial listing from the tutorial engine part of the website created in PHP.

function list_results($form, $offset, $limit) {
global $side;

  if(empty($offset))   $offset=0;  //what tutorial we are starting this page on
  if(empty($limit))    $limit=5;   //number of tutorials per page
  if(empty($form[order]))    $form[order]='visits';  //how will we order the results

  // take form data and query the database
  $key_array=explode(" ",$form[keywords]);

  $keywordDesc="(";
  $keywordTitle="(";
  $keywordCategory="(";

  //prepare a query based on Description, title, or comments
  if(!empty($form[keywords]) && $form[keywords]!=" ") {
	  $keywordDesc=sql_text($form[keywords],'text_search','description');
  	$keywordTitle=sql_text($form[keywords],'text_search','title');
  	$keywordCategory=sql_text($key_array[$i],'text_search','comment');
  }

  //
  if($keywordDesc=="(") $keywordDesc="id != 0";
  if($keywordTitle=="(") $keywordTitle="id != 0";
  if($keywordCategory=="(") $keywordCategory="id = 0";

  $category="id != 0";

  if($form[category_id]!=0) $category=sql_text($form[category_id],'equality','category_id');

  if($form[order]=='date_created') $form[order]=$form[order]." DESC";
  if($form[order]=='rating') $form[order]=$form[order]." DESC";

  //execute query
  $sql="SELECT * FROM tutorials WHERE ($keywordDesc || $keywordTitle) && $category ORDER BY $form[order]";
  $result=sql($sql);
  $numrows=mysql_num_rows($result);

  //execute page sub selection
  $sql="SELECT * FROM tutorials WHERE ($keywordDesc || $keywordTitle) && $category ORDER BY $form[order] LIMIT $offset,$limit";
  $result2=sql($sql);
  $numrows_limit=mysql_num_rows($result2);
  $row=mysql_fetch_array($result2);

  //take result_array and determine how many pages it will take
  $pages= $numrows / $limit;
  if($numrows % $limit) $pages++;

  //figure out math for prev and next buttons
  if($offset>=$limit) $prev=true;
  else               $prev=false;

  $offsetLimit=($pages*$limit) - $limit;
  if(($offset+$limit)<=$offsetLimit) $next=true;
  else                      $next=false;

  $maxoffset=$offset+$limit;
  ($offset-$limit<0)?$minoffset=0:$minoffset=$offset-$limit;
  $start=0;

  if($offset>=(5*$limit)) $start=ceil($offset/$limit)-5;

  if(5*$limit+$offset>=$offsetLimit) {
    $max=ceil($offsetLimit/$limit);
    $start=$max-10;
  } else $max=$start+10;

  //create Prev Next links and each individual page number -- figure out pages
  $listnav="<div align=\"center\" class=\"smalltext\">";
  if($pages!=0 && $offset>0)  $listnav.="<a class=\"links\" href=\"http://www.ascenvia.net/engine/search_tutorial.php?a=search&offset=0&limit=$limit\"><<</a> &nbsp; ";
  if($prev) $listnav.=" <a class=\"links\" href=\"http://www.ascenvia.net/engine/search_tutorial.php?a=search&offset=$minoffset&limit=$limit\"><</a>";
  $listnav.="  &nbsp; &nbsp;  &nbsp;  ";
  if($numrows<=$limit*10) {
    for($l=1;$l<=$pages;$l++) {
      $fix=($l*$limit)-$limit;
      if($fix==$offset) $listnav.="| $l |";
      else $listnav.="| <a class=\"links\"  href=\"http://www.ascenvia.net/engine/search_tutorial.php?a=search&offset=$fix&limit=$limit\">$l</a> |";
    }
  }
  else {
    for($i=($start+1);$i<=$max;$i++) {
      $fix=($i*$limit)-$limit;
      if($fix==$offset) $listnav.="| $i |";
      else $listnav.="| <a class=\"links\"  href=\"http://www.ascenvia.net/engine/search_tutorial.php?a=search&offset=$fix&limit=$limit\">$i</a> |";
    }
  }
  $listnav.=" &nbsp;  &nbsp;  &nbsp; ";
  if($next) $listnav.="<a class=\"links\" href=\"http://www.ascenvia.net/engine/search_tutorial.php?a=search&offset=$maxoffset&limit=$limit\">></a> &nbsp; ";
  $offsetLimit=$offsetLimit-($offsetLimit%$limit);
  if($pages!=0 && $offset<$offsetLimit) $listnav.=" <a class=\"links\" href=\"http://www.ascenvia.net/engine/search_tutorial.php?a=search&offset=$offsetLimit&limit=$limit\">>></a>";
  $listnav.=" </div> ";

  $displaying="Showing $numrows_limit of $numrows";
  $list="";

  $category_array=array();

  for($i=0;$i<$numrows_limit;$i++){

    //load category data for tutorials
    $sql="SELECT name FROM categories WHERE id=$row[category_id]";
    $result3=sql($sql);
    $row2=mysql_fetch_array($result3);

    if(!(in_array($row[category_id],$category_array))) {
         $category_array[]=$row[category_id];
    }

    //load logo data for tutorials
    $sql="SELECT logo_url FROM logos WHERE id=$row[picture_id]";
    $result4=sql($sql);
    $row3=mysql_fetch_array($result4);

    //load domain data for where the tutorial was hosted
    $sql="SELECT name, url FROM domains WHERE id=$row[picture_id]";
    $result5=sql($sql);
    $row4=mysql_fetch_array($result5);

    //display rating data
    if($row[num_votes]!=0) $rating=round($row[votes]/$row[num_votes], 1);
    else $rating="<br><b>Be the First to Rate It</b>";

    $url="http://www.ascenvia.net/engine/framemaker.php?tid=$row[id]";

    //each tutorial will be listed with Tutorial Name at top right
    //with Category and Difficulty right under it.  On left will be
    //Date with Average Rating underneath.

    $data=array( TITLE => $row[title],
                 URL => $url,
                 DESCRIPTION => $row[description],
                 DIFFICULTY => $row[difficulty],
                 RATING => $rating,
                 DATE => $row[date_created],
                 CATEGORY => $row2[name],
                 LOGO_URL => $row3[logo_url],
                 DOMAIN => $row4[name],
                 DOMAIN_URL => $row4[url] );

    //load a template data for search results and fill in the data fields
    $list.=no_display('/base/engine/row.tmp',$data);
    //put template data into a variable to load into the webpage template

    $row=mysql_fetch_array($result2);
  }

  if($numrows==0)  $list="<div class=\"header\">No matches can be found</div>";

  //add controls to change the category and ordering of tutorials
  $array=load_categories();
  $category=put_field('form[category_id]','select',$form[category_id],$array);

  $array2=array( 'rating'=>'Rating','visits'=>'Popularity','date_created'=>'Date Created','difficulty'=>'Difficulty' );
  $order=put_field('form[order]','select',$form[order],$array2);

  //load webpage
  $data=array(
               NAVIGATION=>$listnav,
               SHOWING=>$displaying,
               LIST_RESULTS=>$list  );

}

?>

Result: We created a website for web developers to come for resources on tech topics. The site included news and features, a social web-forum, a search engine and rating system for tutorials, and an admin component for adding content.

ascenvia

This is a high level breakdown of the website’s structure.

pascenvia

This was the ascenvia website when it was accessible to the public.

Click here to explore an archived version of the site.
(Note that images were not uniformly archived.)

Outcomes:

  • The site was released and incorporated.
  • We were able to convince 54 tutorial sites to register nearly 900 tutorials with our tutorial engine.
  • We also had over 1650 members who posted nearly 8500 times on our website’s forum.

Details

Dates: Jun 2001-Apr 2004

Development Skills: HTML, CSS, PHP, MySQL

My Roles: Programming, Vice President

About Me

Who am I?

My name is Will Ryan. I am a UX architect and user researcher. I have a Ph.D. in Informatics from Indiana University where I focused on human-centered design and cognitive science.I love working with technology particularly making it fun, engaging, and easier to learn. My goal is to understand both the technology and the users to ensure these users have meaningful and enjoyable experiences with their technology (whether in software or in hardware—for desktop, web, or mobile).

What do I do?

I specialize in information architecture, UX design, and research, but I have a background in developing both the front- and back-end of computer systems. I have spent years researching learning to use interactive technology, social media, entertainment technology and engagement with interactive technology, and information visualization with over 30 academic publications. I have designed, researched, or developed dozens of websites and about one dozen interactive applications in environments such as Flash, HTML & JQuery, ASP/PHP, and even for the desktop.

I am comfortable talking to users, other designers, artists, programmers, or business people. I love getting my hands “dirty” creating system sketches, planning work flows, writing personas, and creating prototypes. I love being a mouth-piece for user-centered philosophy and talking about UX topics. I have also taught on topics such as web and media design, visual design, project management, and strategic communication. I have worked in UX design and development number of different industries including higher education, consumer electronics, finance, heavy-duty trucking, social media, and food and beverage.

Where am I coming from?

I recently left a career in academics teaching and researching UX design/research and interactive media. I decided my passion was not in teaching these skills, although I do enjoy mentoring others, but I want to be involved in the user experience design process. I have spent the last half year consulting on interactive design projects.

Where do I want to go?

I envision myself leading others in the creation of great interactive software and media one day. I feel my experience in an academic environment has prepared me to lead. I love working with technology and making software that is meaningful for my users and I am excited in engaging in every way possible to achieve this goal. I would like to grow into a leadership position.