diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js
index 8a53bb1b..ca3315a9 100644
--- a/src/node/utils/Minify.js
+++ b/src/node/utils/Minify.js
@@ -39,6 +39,7 @@ var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8'));
 var LIBRARY_WHITELIST = [
       'async'
     , 'security'
+    , 'tinycon'
     , 'underscore'
     , 'unorm'
     ];
diff --git a/src/node/utils/tar.json b/src/node/utils/tar.json
index 74842e9b..36276767 100644
--- a/src/node/utils/tar.json
+++ b/src/node/utils/tar.json
@@ -14,6 +14,7 @@
   , "pad_savedrevs.js"
   , "pad_connectionstatus.js"
   , "chat.js"
+  , "$tinycon/tinycon.js"
   , "excanvas.js"
   , "farbtastic.js"
   ]
diff --git a/src/package.json b/src/package.json
index e61bf3a6..a2fc147a 100644
--- a/src/package.json
+++ b/src/package.json
@@ -31,6 +31,7 @@
                       "slide"               : "1.1.3",
                       "semver"              : "1.0.13",
                       "security"            : "1.0.0",
+                      "tinycon"             : "0.0.1",
                       "underscore"          : "1.3.1",
                       "unorm"               : "1.0.0"
                      },
diff --git a/src/static/js/chat.js b/src/static/js/chat.js
index 9dfc9dc1..486c7256 100644
--- a/src/static/js/chat.js
+++ b/src/static/js/chat.js
@@ -23,7 +23,7 @@
 var padutils = require('./pad_utils').padutils;
 var padcookie = require('./pad_cookie').padcookie;
 
-require('./tinycon');
+var Tinycon = require('tinycon/tinycon');
 
 var chat = (function()
 {
diff --git a/src/static/js/tinycon.js b/src/static/js/tinycon.js
deleted file mode 100644
index 0e6a10a1..00000000
--- a/src/static/js/tinycon.js
+++ /dev/null
@@ -1,237 +0,0 @@
-/*!
- * Tinycon - A small library for manipulating the Favicon
- * Tom Moor, http://tommoor.com
- * Copyright (c) 2012 Tom Moor
- * MIT Licensed
- * @version 0.2.6
-*/
-
-(function(){
-	
-	var Tinycon = {};
-	var currentFavicon = null;
-	var originalFavicon = null;
-	var originalTitle = document.title;
-	var faviconImage = null;
-	var canvas = null;
-	var options = {};
-	var defaults = {
-		width: 7,
-		height: 9,
-		font: '10px arial',
-		colour: '#ffffff',
-		background: '#F03D25',
-		fallback: true
-	};
-	
-	var ua = (function () {
-		var agent = navigator.userAgent.toLowerCase();
-		// New function has access to 'agent' via closure
-		return function (browser) {
-			return agent.indexOf(browser) !== -1;
-		};
-	}());
-
-	var browser = {
-		ie: ua('msie'),
-		chrome: ua('chrome'),
-		webkit: ua('chrome') || ua('safari'),
-		safari: ua('safari') && !ua('chrome'),
-		mozilla: ua('mozilla') && !ua('chrome') && !ua('safari')
-	};
-	
-	// private methods
-	var getFaviconTag = function(){
-		
-		var links = document.getElementsByTagName('link');
-		
-		for(var i=0, len=links.length; i < len; i++) {
-			if ((links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
-				return links[i];
-			}
-		}
-		
-		return false;
-	};
-	
-	var removeFaviconTag = function(){
-	
-		var links = document.getElementsByTagName('link');
-		var head = document.getElementsByTagName('head')[0];
-		
-		for(var i=0, len=links.length; i < len; i++) {
-			var exists = (typeof(links[i]) !== 'undefined');
-			if (exists && links[i].getAttribute('rel') === 'icon') {
-				head.removeChild(links[i]);
-			}
-		}
-	};
-	
-	var getCurrentFavicon = function(){
-		
-		if (!originalFavicon || !currentFavicon) {
-			var tag = getFaviconTag();
-			originalFavicon = currentFavicon = tag ? tag.getAttribute('href') : '/favicon.ico';
-		}
-
-		return currentFavicon;
-	};
-	
-	var getCanvas = function (){
-		
-		if (!canvas) {
-			canvas = document.createElement("canvas");
-			canvas.width = 16;
-			canvas.height = 16;
-		}
-		
-		return canvas;
-	};
-	
-	var setFaviconTag = function(url){
-		removeFaviconTag();
-		
-		var link = document.createElement('link');
-		link.type = 'image/x-icon';
-		link.rel = 'icon';
-		link.href = url;
-		document.getElementsByTagName('head')[0].appendChild(link);
-	};
-	
-	var log = function(message){
-		if (window.console) window.console.log(message);
-	};
-	
-	var drawFavicon = function(num, colour) {
-
-		// fallback to updating the browser title if unsupported
-		if (!getCanvas().getContext || browser.ie || browser.safari || options.fallback === 'force') {
-			return updateTitle(num);
-		}
-		
-		var context = getCanvas().getContext("2d");
-		var colour = colour || '#000000';
-		var num = num || 0;
-		var src = getCurrentFavicon();
-		
-		faviconImage = new Image();
-		faviconImage.onload = function() {
-			
-			// clear canvas  
-			context.clearRect(0, 0, 16, 16);
-
-			// draw original favicon
-			context.drawImage(faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, 16, 16);
-			
-			// draw bubble over the top
-			if (num > 0) drawBubble(context, num, colour);
-			
-			// refresh tag in page
-			refreshFavicon();
-		};
-		
-		// allow cross origin resource requests if the image is not a data:uri
-		// as detailed here: https://github.com/mrdoob/three.js/issues/1305
-		if (!src.match(/^data/)) {
-			faviconImage.crossOrigin = 'anonymous';
-		}
-		
-		faviconImage.src = src;
-	};
-	
-	var updateTitle = function(num) {
-		
-		if (options.fallback) {
-			if (num > 0) {
-				document.title = '('+num+') ' + originalTitle;
-			} else {
-				document.title = originalTitle;
-			}
-		}
-	};
-	
-	var drawBubble = function(context, num, colour) {
-		
-		// bubble needs to be larger for double digits
-		var len = (num+"").length-1;
-		var width = options.width + (6*len);
-		var w = 16-width;
-		var h = 16-options.height;
-
-		// webkit seems to render fonts lighter than firefox
-		context.font = (browser.webkit ? 'bold ' : '') + options.font;
-		context.fillStyle = options.background;
-		context.strokeStyle = options.background;
-		context.lineWidth = 1;
-		
-		// bubble
-		context.fillRect(w,h,width-1,options.height);
-		
-		// rounded left
-		context.beginPath();
-		context.moveTo(w-0.5,h+1);
-		context.lineTo(w-0.5,15);
-		context.stroke();
-		
-		// rounded right
-		context.beginPath();
-		context.moveTo(15.5,h+1);
-		context.lineTo(15.5,15);
-		context.stroke();
-		
-		// bottom shadow
-		context.beginPath();
-		context.strokeStyle = "rgba(0,0,0,0.3)";
-		context.moveTo(w,16);
-		context.lineTo(15,16);
-		context.stroke();
-		
-		// number
-		context.fillStyle = options.colour;
-		context.textAlign = "right";
-		context.textBaseline = "top";
-		
-		// unfortunately webkit/mozilla are a pixel different in text positioning
-		context.fillText(num, 15, browser.mozilla ? 7 : 6);  
-	};
-	
-	var refreshFavicon = function(){
-		// check support
-		if (!getCanvas().getContext) return;
-		
-		setFaviconTag(getCanvas().toDataURL());
-	};
-	
-	
-	// public methods
-	Tinycon.setOptions = function(custom){
-		options = {};
-		
-		for(var key in defaults){
-			options[key] = custom.hasOwnProperty(key) ? custom[key] : defaults[key];
-		}
-		return this;
-	};
-	
-	Tinycon.setImage = function(url){
-		currentFavicon = url;
-		refreshFavicon();
-		return this;
-	};
-	
-	Tinycon.setBubble = function(num, colour){
-		
-		// validate
-		if(isNaN(parseFloat(num)) || !isFinite(num)) return log('Bubble must be a number');
-		
-		drawFavicon(num, colour);
-		return this;
-	};
-	
-	Tinycon.reset = function(){
-		Tinycon.setImage(originalFavicon);
-	};
-	
-	Tinycon.setOptions(defaults);
-	window.Tinycon = Tinycon;
-})();