diff --git a/app.js b/app.js
index 783a4b04e008334e225bdadbf57018f051e3d444..1b16fd42300f0e14d4239341c87f3dc53d11bdd1 100644
--- a/app.js
+++ b/app.js
@@ -21,22 +21,21 @@ var net = require('net');
 var dgram = require('dgram');
 var fs = require('fs');
 var gm = require('gm');
-
-/* Express */
-
-var express = require('express');
-var path = require('path');
 var favicon = require('serve-favicon');
 var logger = require('morgan');
 var cookieParser = require('cookie-parser');
 var bodyParser = require('body-parser');
 
+var express = require('express');
+var app = express();
+var server = http.createServer(app);
+
+var io = require('socket.io')(server);
+
 var routes = require('./routes/index');
 var latestPhoto = require('./routes/latest');
 var allPhotos = require('./routes/all');
 
-var app = express();
-
 process.title = 'WiPho';
 var gracefulShutdown = function() {
   console.log("Shutting down...");
@@ -44,7 +43,9 @@ var gracefulShutdown = function() {
 }
 
 process.on ('SIGTERM', gracefulShutdown);
-process.on ('SIGINT', gracefulShutdown);   
+process.on ('SIGINT', gracefulShutdown);
+
+app.set('port', config.httpPort || 3000);
 
 // view engine setup
 app.set('views', path.join(__dirname, 'views'));
@@ -95,6 +96,25 @@ app.use(function(err, req, res, next) {
 
 module.exports = app;
 
+io.on('connection', function (socket) {
+  //console.log('Display connected!');
+  socket.on('disconnect', function(){
+    //console.log('Display disconnected');
+  });
+  socket.on('display', function(data) {
+    if(data.status == 'success') {
+      console.log('Photo displayed!');
+    } else {
+      console.log('Photo not displayed!');
+    }
+  });
+});
+
+server.listen(app.get('port'), function() {
+  console.log("WiPho is listening on port " + app.get('port'));
+});
+
+
 /* WiPho */
 
 photos = new Array();
@@ -108,227 +128,228 @@ findCard();
 
 function downloadPhotos() {
 
-	if(alreadyDownloading == true || downloadList.length < 1)
-		return true;
-		
-	alreadyDownloading = true;
-	var photo = downloadList.pop();
-	var localFile = pathPhotos+'/'+photo;
-	var localPreview = pathPreviews+'/'+photo;
-	console.log('['+photo+'] Downloading from http://'+cardAddr+cardPath+'/'+photo);
-	
-	var file = fs.createWriteStream(localFile);
-		
-	file.on('error', function(err) {
-		console.log("FS: "+err);
-	});
-	
-	file.on('finish', function() {
-		file.close();
-		alreadyDownloading = false;
-		console.log('['+photo+'] Saved as '+localFile);
-		
-		gm(localFile).autoOrient().resize(previewWidth, previewHeight).write(localPreview, function (err) {
-			if (!err) {
-				console.log('['+photo+'] Resized to '+previewWidth+'x'+previewHeight);						
-				if(photos.length == 0 || photo != photos[photos.length-1].name) {
-					photos.push({id: photoIndex, name: photo});
-					photoIndex++;
-				}
-			
-			}else{
-				console.log('Photo resize error: '+err);
-			}
-			
-		});
-
-		if(downloadPrevious == true) {
-			getPhotoList();
-		}
-		
-		if(downloadList.length > 0) {
-			downloadPhotos();
-		}else{
-			console.log("All photos downloaded, waiting for new ones...");
-		}
-		
-	});
-	
-	var options = {
-		hostname: cardAddr,
-		port: 80,
-		path: cardPath+'/'+photo,
-		method: 'GET'
-	};
-	
-	var request = http.get(options, function(response) {
-	  response.pipe(file);
-	});
-	
-	request.on('error', function(e) {
-		console.log("HTTP Error: " + e.message);
-	});
+  if(alreadyDownloading == true || downloadList.length < 1)
+    return true;
+    
+  alreadyDownloading = true;
+  var photo = downloadList.pop();
+  var localFile = pathPhotos+'/'+photo;
+  var localPreview = pathPreviews+'/'+photo;
+  console.log('['+photo+'] Downloading from http://'+cardAddr+cardPath+'/'+photo);
+  
+  var file = fs.createWriteStream(localFile);
+    
+  file.on('error', function(err) {
+    console.log("FS: "+err);
+  });
+  
+  file.on('finish', function() {
+    file.close();
+    alreadyDownloading = false;
+    console.log('['+photo+'] Saved as '+localFile);
+    
+    gm(localFile).autoOrient().resize(previewWidth, previewHeight).write(localPreview, function (err) {
+      if (!err) {
+        console.log('['+photo+'] Resized to '+previewWidth+'x'+previewHeight);            
+        if(photos.length == 0 || photo != photos[photos.length-1].name) {
+          photos.push({id: photoIndex, name: photo});
+          photoIndex++;
+          io.emit('photo', { path: photo });
+        }
+      
+      }else{
+        console.log('Photo resize error: '+err);
+      }
+      
+    });
+
+    if(downloadPrevious == true) {
+      getPhotoList();
+    }
+    
+    if(downloadList.length > 0) {
+      downloadPhotos();
+    }else{
+      console.log("All photos downloaded, waiting for new ones...");
+    }
+    
+  });
+  
+  var options = {
+    hostname: cardAddr,
+    port: 80,
+    path: cardPath+'/'+photo,
+    method: 'GET'
+  };
+  
+  var request = http.get(options, function(response) {
+    response.pipe(file);
+  });
+  
+  request.on('error', function(e) {
+    console.log("HTTP Error: " + e.message);
+  });
 
 }
 
 function getPhotoList() {
 
-	downloadPrevious = false;
-
-	var options = {
-		host: cardAddr,
-		port: 80,
-		path: '/cgi-bin/tslist?PATH=/www'+cardPath+'&keepfresh='+Date.now().toString()
-	};
-
-	http.get(options, function(resp){
-		console.log("Getting list of photos on card...");
-		resp.on('data', function(data){
-			var strFiles = data.toString().split(os.EOL)[2];
-			var regex = /FileName\d+=([a-zA-Z0-9_\.]+)&FileType\d+=File&/g;
-			var arrPhotos = new Array();
-			while (match = regex.exec(strFiles)) {
-				arrPhotos.push(match[1]);
-			}
-			var i = 0;
-			arrPhotos.forEach(function(photo) {
-				fs.exists(pathPhotos+'/'+photo, function(exists) {
-					if (exists) {
-						//console.log('['+photo+'] Photo '+photo+' already downloaded!');
-					}else{
-						console.log('['+photo+'] Photo '+photo+' not downloaded yet, adding to download list!');
-						downloadList.push(photo);
-					}
-					i++;
-					if(i == arrPhotos.length-1) {
-						if(downloadList.length > 0) {
-							downloadPhotos();
-						}else{
-							console.log("All photos already downloaded!");
-						}
-					}
-				});
-			});
-		});
-	}).on("error", function(e){
-		console.log("Error getting photo list: " + e.message);
-		getPhotoList();
-	});
+  downloadPrevious = false;
+
+  var options = {
+    host: cardAddr,
+    port: 80,
+    path: '/cgi-bin/tslist?PATH=/www'+cardPath+'&keepfresh='+Date.now().toString()
+  };
+
+  http.get(options, function(resp){
+    console.log("Getting list of photos on card...");
+    resp.on('data', function(data){
+      var strFiles = data.toString().split(os.EOL)[2];
+      var regex = /FileName\d+=([a-zA-Z0-9_\.]+)&FileType\d+=File&/g;
+      var arrPhotos = new Array();
+      while (match = regex.exec(strFiles)) {
+        arrPhotos.push(match[1]);
+      }
+      var i = 0;
+      arrPhotos.forEach(function(photo) {
+        fs.exists(pathPhotos+'/'+photo, function(exists) {
+          if (exists) {
+            //console.log('['+photo+'] Photo '+photo+' already downloaded!');
+          }else{
+            console.log('['+photo+'] Photo '+photo+' not downloaded yet, adding to download list!');
+            downloadList.push(photo);
+          }
+          i++;
+          if(i == arrPhotos.length-1) {
+            if(downloadList.length > 0) {
+              downloadPhotos();
+            }else{
+              console.log("All photos already downloaded!");
+            }
+          }
+        });
+      });
+    });
+  }).on("error", function(e){
+    console.log("Error getting photo list: " + e.message);
+    getPhotoList();
+  });
 
 }
 
 
 function enableShootAndView(ip) {
 
-	var client = net.connect({port: 5566, host: ip}, function() {
-		console.log('Enabling Shoot & View...');
-	});
-	
-	client.on('connect', function() {
-		console.log('Shoot & View enabled, waiting for photos...');
-		if(cardPath != null) {
-			getPhotoList();
-		}
-	});
-	
-	client.on('error', function(err) {
-		console.log('Shoot & View error: '+err);
-		findCard();
-	});
-	
-	client.on('data', function(data) {
-		var path = data.toString().substr(5).replace(/\0/g, '');
-		var photo = path.split('/').pop();
-		cardPath = path.substring(0, path.lastIndexOf('/'));
-		downloadList.push(photo);
-		downloadPhotos();
-	});
-
-	client.on('end', function() {
-		console.log('Shoot & View stopped!');
-	});
+  var client = net.connect({port: 5566, host: ip}, function() {
+    console.log('Enabling Shoot & View...');
+  });
+  
+  client.on('connect', function() {
+    console.log('Shoot & View enabled, waiting for photos...');
+    if(cardPath != null) {
+      getPhotoList();
+    }
+  });
+  
+  client.on('error', function(err) {
+    console.log('Shoot & View error: '+err);
+    findCard();
+  });
+  
+  client.on('data', function(data) {
+    var path = data.toString().substr(5).replace(/\0/g, '');
+    var photo = path.split('/').pop();
+    cardPath = path.substring(0, path.lastIndexOf('/'));
+    downloadList.push(photo);
+    downloadPhotos();
+  });
+
+  client.on('end', function() {
+    console.log('Shoot & View stopped!');
+  });
 
 }
 
 
 function pingCard(ip) {
 
-	req = http.get('http://'+ip+'/', function(res) {
-		//console.log('Card is alive!');
-		req.destroy();
-	});
-	
-	req.on('error', function(err) {
-		cardFound = false;
-		console.log('ERROR: ' + err);
-		req.destroy();
-		clearInterval(itvPing);
-		findCard();
-	});
-	
-	req.setTimeout(5000, function() {
-		cardFound = false;
-		downloadPrevious = true;
-		console.log('Card has disappeared!');
-		req.destroy();
-		clearInterval(itvPing);
-		findCard();
-	});
-	
+  req = http.get('http://'+ip+'/', function(res) {
+    //console.log('Card is alive!');
+    req.destroy();
+  });
+  
+  req.on('error', function(err) {
+    cardFound = false;
+    console.log('ERROR: ' + err);
+    req.destroy();
+    clearInterval(itvPing);
+    findCard();
+  });
+  
+  req.setTimeout(5000, function() {
+    cardFound = false;
+    downloadPrevious = true;
+    console.log('Card has disappeared!');
+    req.destroy();
+    clearInterval(itvPing);
+    findCard();
+  });
+  
 }
 
 
 function findCard() {
 
-	if(alreadySearching == true)
-		return;
-	else
-		alreadySearching = true;
-		
-	console.log("Searching for card...");
-
-	var socket = dgram.createSocket('udp4');
-	var message = new Buffer('dummy');
-	var itvSearch;
-	
-	socket.bind(58255, function() {
-		socket.setBroadcast(true);
-	});
-	
-	socket.on('error', function (err) {
-		console.log("socket error:\n" + err.stack);
-		socket.close();
-		findCard();
-	});
-
-	socket.on('message', function (msg, rinfo) {
-		clearInterval(itvSearch);
-		socket.close();
-		msg = msg.toString();
-		cardAddr = msg.match(/ip=(.*)/)[1];
-		cardFound = true;
-		console.log("Found card on "+cardAddr);
-		enableShootAndView(cardAddr);
-		itvPing = setInterval(function() {
-			pingCard(cardAddr);
-		}, 5000);
-		alreadySearching = false;
-	});
-
-	socket.on('listening', function () {
-		var address = socket.address();
-		sendSearch();
-		itvSearch = setInterval(function() {
-			sendSearch();
-		}, 2000);
-		
-		function sendSearch() {
-			socket.send(message, 0, message.length, 55777, cardAddr, function(err, bytes) {
-				if(err != null)
-					console.log("socket error:\n" + err.stack);
-			});
-		}
-		
-	});
-	
+  if(alreadySearching == true)
+    return;
+  else
+    alreadySearching = true;
+    
+  console.log("Searching for card...");
+
+  var socket = dgram.createSocket('udp4');
+  var message = new Buffer('dummy');
+  var itvSearch;
+  
+  socket.bind(58255, function() {
+    socket.setBroadcast(true);
+  });
+  
+  socket.on('error', function (err) {
+    console.log("socket error:\n" + err.stack);
+    socket.close();
+    findCard();
+  });
+
+  socket.on('message', function (msg, rinfo) {
+    clearInterval(itvSearch);
+    socket.close();
+    msg = msg.toString();
+    cardAddr = msg.match(/ip=(.*)/)[1];
+    cardFound = true;
+    console.log("Found card on "+cardAddr);
+    enableShootAndView(cardAddr);
+    itvPing = setInterval(function() {
+      pingCard(cardAddr);
+    }, 5000);
+    alreadySearching = false;
+  });
+
+  socket.on('listening', function () {
+    var address = socket.address();
+    sendSearch();
+    itvSearch = setInterval(function() {
+      sendSearch();
+    }, 2000);
+    
+    function sendSearch() {
+      socket.send(message, 0, message.length, 55777, cardAddr, function(err, bytes) {
+        if(err != null)
+          console.log("socket error:\n" + err.stack);
+      });
+    }
+    
+  });
+  
 }
\ No newline at end of file
diff --git a/bin/www b/bin/www
deleted file mode 100644
index 8a267d266f2812c7d8c92a2c494d855811714dae..0000000000000000000000000000000000000000
--- a/bin/www
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env node
-
-var config = require('../config.json');
-
-var debug = require('debug')('foo');
-var app = require('../app');
-
-app.set('port', config.httpPort || 3000);
-
-var server = app.listen(app.get('port'), function() {
-  console.log("Express server listening on port " + app.get('port'));
-});
diff --git a/package.json b/package.json
index 7962b6e68cc6ec7040888b4c2e545015df827db1..392186c49b52d0b014acd3dc7778b3a6034e9fc2 100644
--- a/package.json
+++ b/package.json
@@ -3,17 +3,18 @@
   "version": "0.0.1",
   "private": true,
   "scripts": {
-    "start": "node ./bin/www",
+    "start": "node app.js",
     "stop": "pkill --signal SIGINT WiPho"
   },
   "dependencies": {
-    "express": "~4.9.0",
-    "body-parser": "~1.8.1",
+    "body-parser": "^1.12.0",
     "cookie-parser": "~1.3.3",
-    "morgan": "~1.3.0",
-    "serve-favicon": "~2.1.3",
-    "debug": "~2.0.0",
-    "jade": "~1.6.0",
-    "gm": "~1.16.0"
+    "debug": "^0.7.0",
+    "express": "~4.12.2",
+    "gm": "^1.17.0",
+    "jade": "^1.9.2",
+    "morgan": "^1.5.1",
+    "serve-favicon": "^2.2.0",
+    "socket.io": "^1.3.5"
   }
 }
diff --git a/public/javascripts/wipho.js b/public/javascripts/wipho.js
index c102f31e28d748fda0a5bbc8d46abfcd600759d8..7f58ef4af63af57a6e26e3408025f57cf0bc9bc2 100644
--- a/public/javascripts/wipho.js
+++ b/public/javascripts/wipho.js
@@ -1,13 +1,11 @@
+
+var socket = io.connect();
+
 $(document).ready(function() {
 
   $('body').height($(window).height());
   centerImage();
 
-  getLatest();
-  setInterval(function() {
-    getLatest();
-  }, 1000);
-
   $(window).resize(function () {
     centerImage();
   });
@@ -22,18 +20,15 @@ $(document).ready(function() {
     centerImage();
     $(this).animate({opacity: 1}, 400);
   });
-  
-});
 
-function getLatest() {
-  $.getJSON('/latest', function(data) {
-    if('/previews/'+data.name != $('#latest').attr('src')) {
-      $('#latest').animate({opacity: 0}, 400, function() {
-        $(this).attr('src', '/previews/'+data.name);
-      });
-    }
+  socket.on('photo', function (data) {
+    $('#latest').animate({opacity: 0}, 400, function() {
+      $(this).attr('src', '/previews/'+data.path);
+      socket.emit('display', {status: 'success'});
+    });
   });
-}
+
+});
 
 function centerImage() {
   $('#latest').css({
@@ -41,4 +36,4 @@ function centerImage() {
     left: ($(window).width() - $('#latest').outerWidth()) / 2,
     top: ($(window).height() - $('#latest').outerHeight()) / 2,
   });
-}
\ No newline at end of file
+}
diff --git a/routes/index.js b/routes/index.js
index 823aaa54cb4ddd9c5cff3aeda096271f75c44727..78527924cde621ce2967ee7fc6fc650f0c31414b 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -1,9 +1,13 @@
+var config = require('../config.json');
+
 var express = require('express');
 var router = express.Router();
 
 /* GET home page. */
 router.get('/', function(req, res) {
-  res.render('index', { title: 'WiPho' });
+  res.render('index', { 
+    title: 'WiPho'
+  });
 });
 
 module.exports = router;
diff --git a/views/layout.jade b/views/layout.jade
index 5c80b5896751dfbef618219b74034eb5b40c358a..60091bbd3bc1ef02191022eeba03e655d434c2cf 100644
--- a/views/layout.jade
+++ b/views/layout.jade
@@ -2,8 +2,9 @@ doctype html
 html
   head
     title= title
+    script(src='/socket.io/socket.io.js')
     script(src='/javascripts/jquery-1.11.1.min.js')
     script(src='/javascripts/wipho.js')
     link(rel='stylesheet', href='/stylesheets/wipho.css')
   body
-    block content
\ No newline at end of file
+    block content