diff --git a/.htaccess b/.htaccess
index 306a8cc..f964248 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1,3 +1,4 @@
-# Block access to .git directory
-RedirectMatch 404 /\.git
-Options -Indexes
\ No newline at end of file
+# Block access to .git directory
+RedirectMatch 404 /\.git
+Options -Indexes
+ErrorDocument 404 /404/index.html
\ No newline at end of file
diff --git a/404/.gitignore b/404/.gitignore
new file mode 100644
index 0000000..ec8e01f
--- /dev/null
+++ b/404/.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+.svn
+log/*.log
+tmp/**
+dist
+netmag_files
+netmag.html
diff --git a/404/LICENSE b/404/LICENSE
new file mode 100644
index 0000000..28cfd7d
--- /dev/null
+++ b/404/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2011 Hakim El Hattab, http://hakim.se
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/404/README.md b/404/README.md
new file mode 100644
index 0000000..23878e0
--- /dev/null
+++ b/404/README.md
@@ -0,0 +1,11 @@
+# 404
+
+This 404 page was created for netmagazine.com.
+
+[Check out the demo](http://hakim.se/experiments/html5/404) or see how it looked in context on [netmagazine.com](http://hakim.se/experiments/html5/404/netmag.html).
+
+# License
+
+MIT licensed
+
+Copyright (C) 2011 Hakim El Hattab, http://hakim.se
diff --git a/404/css/404.css b/404/css/404.css
new file mode 100644
index 0000000..9adaadf
--- /dev/null
+++ b/404/css/404.css
@@ -0,0 +1,25 @@
+#fof {
+ margin: 20px 0 120px;
+ text-align: center;
+}
+
+#fof div {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+
+ background: #000;
+}
+
+#fof canvas,
+#fof img {
+ position: relative;
+ width: 960px;
+ height: 480px;
+ margin-bottom: 20px;
+ z-index: 1;
+
+ background: #000;
+}
\ No newline at end of file
diff --git a/404/css/main.css b/404/css/main.css
new file mode 100644
index 0000000..f6c603c
--- /dev/null
+++ b/404/css/main.css
@@ -0,0 +1,68 @@
+/**
+ * @author Hakim El Hattab
+ */
+
+
+/*********************************************
+ * GLOBAL STYLES
+ *********************************************/
+
+html, body {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+
+ background: #000;
+ color: #000;
+
+ font-family: 'Molengo', Arial, Helvetica, sans-serif;
+}
+
+
+/*********************************************
+ * EXPERIMENTS HEADER
+ *********************************************/
+
+a {
+ color: rgb(220,200,150);
+}
+
+header {
+ padding: 10px 0;
+ z-index: 999;
+
+ height: 20px;
+ width: 100%;
+
+ background: rgba(0,0,0,0.7);
+}
+
+header>div {
+ float: right;
+ margin-right: 10px;
+}
+
+header div.facebook-button {
+ top: 1px;
+}
+
+header a {
+ float: left;
+ font-size: 14px;
+ color: #eee;
+ margin-left: 10px;
+}
+
+/*********************************************
+ * CONTENTS
+ *********************************************/
+
+#fof {
+ margin-top: 40px;
+ text-align: center;
+}
+
+
+
+
+
diff --git a/404/css/reset.css b/404/css/reset.css
new file mode 100644
index 0000000..68f227a
--- /dev/null
+++ b/404/css/reset.css
@@ -0,0 +1,57 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+
+/* HTML5BP:
+ These selection declarations have to be separate.
+ No text-shadow: twitter.com/miketaylr/status/12228805301
+ Also: hot pink. */
+::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; }
+::selection { background:#FF5E99; color:#fff; text-shadow: none; }
+
diff --git a/404/fallback.png b/404/fallback.png
new file mode 100644
index 0000000..fd972e9
Binary files /dev/null and b/404/fallback.png differ
diff --git a/404/index.html b/404/index.html
new file mode 100644
index 0000000..b4b58a8
--- /dev/null
+++ b/404/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ 404
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/404/js/404.js b/404/js/404.js
new file mode 100644
index 0000000..b499eb3
--- /dev/null
+++ b/404/js/404.js
@@ -0,0 +1,312 @@
+
+/**
+ * Copyright (C) 2011 Hakim El Hattab, http://hakim.se
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author Hakim El Hattab | http://hakim.se
+ */
+(function(){
+
+ var DISPLAY_WIDTH = 960,
+ DISPLAY_HEIGHT = 480,
+ DISPLAY_DURATION = 10,
+ OVERLAY_DURATION = 3;
+
+ var mouse = { x: 0, y: 0 },
+ container,
+ overlay,
+ overlayOpacity = 1,
+ canvas,
+ context,
+ startTime,
+ eyes;
+
+ function initialize() {
+ container = document.getElementById( 'fof' );
+ overlay = document.querySelector( '#fof>div' );
+ canvas = document.querySelector( '#fof>canvas' );
+
+ if( canvas ) {
+ canvas.width = DISPLAY_WIDTH;
+ canvas.height = DISPLAY_HEIGHT;
+
+ context = canvas.getContext( '2d' );
+
+ document.addEventListener( 'mousemove', function( event ) {
+ mouse.x = event.clientX;
+ mouse.y = event.clientY;
+ }, false );
+
+ eyes = [
+ new Eye( canvas, 0.50, 0.50, 5.00, 0.10 ),
+
+ new Eye( canvas, 0.19, 0.80, 0.88, 0.31 ),
+ new Eye( canvas, 0.10, 0.54, 0.84, 0.32 ),
+ new Eye( canvas, 0.81, 0.13, 0.63, 0.33 ),
+ new Eye( canvas, 0.89, 0.19, 0.58, 0.34 ),
+ new Eye( canvas, 0.40, 0.08, 0.97, 0.35 ),
+ new Eye( canvas, 0.64, 0.74, 0.57, 0.36 ),
+ new Eye( canvas, 0.41, 0.89, 0.56, 0.37 ),
+ new Eye( canvas, 0.92, 0.89, 0.75, 0.38 ),
+ new Eye( canvas, 0.27, 0.20, 0.87, 0.39 ),
+ new Eye( canvas, 0.17, 0.46, 0.68, 0.41 ),
+ new Eye( canvas, 0.71, 0.29, 0.93, 0.42 ),
+ new Eye( canvas, 0.84, 0.46, 0.54, 0.43 ),
+ new Eye( canvas, 0.93, 0.35, 0.63, 0.44 ),
+ new Eye( canvas, 0.77, 0.82, 0.85, 0.45 ),
+ new Eye( canvas, 0.36, 0.74, 0.90, 0.46 ),
+ new Eye( canvas, 0.13, 0.24, 0.85, 0.47 ),
+ new Eye( canvas, 0.58, 0.20, 0.77, 0.48 ),
+ new Eye( canvas, 0.55, 0.84, 0.87, 0.50 ),
+ ];
+
+ startTime = Date.now();
+
+ animate();
+ }
+ else if( overlay ) {
+ overlay.parentElement.removeChild( overlay );
+ }
+ }
+
+ function animate() {
+ // The number of seconds that have passed since initialization
+ var seconds = ( Date.now() - startTime ) / 1000;
+
+ // Out with the old ...
+ context.clearRect( 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT );
+
+ // ... in with the new
+ for( var i = 0, len = eyes.length; i < len; i++ ) {
+ var eye = eyes[i];
+
+ if( seconds > eye.activationTime * DISPLAY_DURATION ) {
+ eye.activate();
+ };
+
+ eye.update( mouse );
+ }
+
+ // Remove the overlay if its time has passed
+ if( seconds > OVERLAY_DURATION && overlay !== undefined ) {
+
+ // Ease-in
+ overlayOpacity *= 0.94 + ( 0.055 * overlayOpacity );
+ overlayOpacity = Math.max( overlayOpacity - 0.01, 0 );
+
+ overlay.style.opacity = overlayOpacity;
+
+ if( overlayOpacity === 0 ) {
+ // We have no more use for the overlay, removing it ensures
+ // that we do not repeatedly enter this if statement
+ container.removeChild( overlay );
+
+ overlay = undefined;
+ }
+ }
+
+ requestAnimFrame( animate );
+ }
+
+ window.addEventListener ? window.addEventListener( 'load', initialize, false ) : window.onload = initialize;
+
+})();
+
+
+function Eye( canvas, x, y, scale, time ) {
+ this.canvas = canvas;
+ this.context = this.canvas.getContext( '2d' )
+
+ // The time at which this eye will come alive
+ this.activationTime = time;
+
+ // The speed at which the iris follows the mouse
+ this.irisSpeed = 0.01 + ( Math.random() * 0.2 ) / scale;
+
+ // The speed at which the eye opens and closes
+ this.blinkSpeed = 0.2 + ( Math.random() * 0.2 );
+ this.blinkInterval = 5000 + 5000 * ( Math.random() );
+
+ // Timestamp of the last blink
+ this.blinkTime = Date.now();
+
+ this.scale = scale;
+ this.size = 70 * scale;
+
+ this.x = x * canvas.width;
+ this.y = y * canvas.height + ( this.size * 0.15 );
+
+ this.iris = {
+ x: this.x,
+ y: this.y - ( this.size * 0.1 ),
+ size: this.size * 0.2
+ };
+
+ this.pupil = {
+ width: 2 * scale,
+ height: this.iris.size * 0.75
+ };
+
+ this.exposure = {
+ top: 0.1 + ( Math.random() * 0.3 ),
+ bottom: 0.5 + ( Math.random() * 0.3 ),
+ current: 0,
+ target: 1
+ };
+
+ // Affects the amount of inner shadow
+ this.tiredness = ( 0.5 - this.exposure.top ) + 0.1;
+
+ this.isActive = false;
+
+ this.activate = function() {
+ this.isActive = true;
+ }
+
+ this.update = function( mouse ) {
+ if( this.isActive === true ) {
+ this.render( mouse );
+ }
+ }
+
+ this.render = function( mouse ) {
+ var time = Date.now();
+
+ if( this.exposure.current < 0.012 ) {
+ this.exposure.target = 1;
+ }
+ else if( time - this.blinkTime > this.blinkInterval ) {
+ this.exposure.target = 0;
+ this.blinkTime = time;
+ }
+
+ this.exposure.current += ( this.exposure.target - this.exposure.current ) * this.blinkSpeed;
+
+ // Eye left/right
+ var el = { x: this.x - ( this.size * 0.8 ), y: this.y - ( this.size * 0.1 ) };
+ var er = { x: this.x + ( this.size * 0.8 ), y: this.y - ( this.size * 0.1 ) };
+
+ // Eye top/bottom
+ var et = { x: this.x, y: this.y - ( this.size * ( 0.5 + ( this.exposure.top * this.exposure.current ) ) ) };
+ var eb = { x: this.x, y: this.y - ( this.size * ( 0.5 - ( this.exposure.bottom * this.exposure.current ) ) ) };
+
+ // Eye inner shadow top
+ var eit = { x: this.x, y: this.y - ( this.size * ( 0.5 + ( ( 0.5 - this.tiredness ) * this.exposure.current ) ) ) };
+
+ // Eye iris
+ var ei = { x: this.x, y: this.y - ( this.iris.size ) };
+
+ // Offset the iris depending on mouse position
+ var eio = {
+ x: ( mouse.x - ei.x ) / ( window.innerWidth - ei.x ),
+ y: ( mouse.y ) / ( window.innerHeight )
+ };
+
+ // Apply the iris offset
+ ei.x += eio.x * 16 * Math.max( 1, this.scale * 0.4 );
+ ei.y += eio.y * 10 * Math.max( 1, this.scale * 0.4 );
+
+ this.iris.x += ( ei.x - this.iris.x ) * this.irisSpeed;
+ this.iris.y += ( ei.y - this.iris.y ) * this.irisSpeed;
+
+ // Eye fill drawing
+ this.context.fillStyle = 'rgba(255,255,255,1.0)';
+ this.context.strokeStyle = 'rgba(100,100,100,1.0)';
+ this.context.beginPath();
+ this.context.lineWidth = 3;
+ this.context.lineJoin = 'round';
+ this.context.moveTo( el.x, el.y );
+ this.context.quadraticCurveTo( et.x, et.y, er.x, er.y );
+ this.context.quadraticCurveTo( eb.x, eb.y, el.x, el.y );
+ this.context.closePath();
+ this.context.stroke();
+ this.context.fill();
+
+ // Iris
+ this.context.save();
+ this.context.globalCompositeOperation = 'source-atop';
+ this.context.translate(this.iris.x*0.1,0);
+ this.context.scale(0.9,1);
+ this.context.strokeStyle = 'rgba(0,0,0,0.5)';
+ this.context.fillStyle = 'rgba(130,50,90,0.9)';
+ this.context.lineWidth = 2;
+ this.context.beginPath();
+ this.context.arc(this.iris.x, this.iris.y, this.iris.size, 0, Math.PI*2, true);
+ this.context.fill();
+ this.context.stroke();
+ this.context.restore();
+
+ // Iris inner
+ this.context.save();
+ this.context.shadowColor = 'rgba(255,255,255,0.5)';
+ this.context.shadowOffsetX = 0;
+ this.context.shadowOffsetY = 0;
+ this.context.shadowBlur = 2 * this.scale;
+ this.context.globalCompositeOperation = 'source-atop';
+ this.context.translate(this.iris.x*0.1,0);
+ this.context.scale(0.9,1);
+ this.context.fillStyle = 'rgba(255,255,255,0.2)';
+ this.context.beginPath();
+ this.context.arc(this.iris.x, this.iris.y, this.iris.size * 0.7, 0, Math.PI*2, true);
+ this.context.fill();
+ this.context.restore();
+
+ // Pupil
+ this.context.save();
+ this.context.globalCompositeOperation = 'source-atop';
+ this.context.fillStyle = 'rgba(0,0,0,0.9)';
+ this.context.beginPath();
+ this.context.moveTo( this.iris.x, this.iris.y - ( this.pupil.height * 0.5 ) );
+ this.context.quadraticCurveTo( this.iris.x + ( this.pupil.width * 0.5 ), this.iris.y, this.iris.x, this.iris.y + ( this.pupil.height * 0.5 ) );
+ this.context.quadraticCurveTo( this.iris.x - ( this.pupil.width * 0.5 ), this.iris.y, this.iris.x, this.iris.y - ( this.pupil.height * 0.5 ) );
+ this.context.fill();
+ this.context.restore();
+
+ this.context.save();
+ this.context.shadowColor = 'rgba(0,0,0,0.9)';
+ this.context.shadowOffsetX = 0;
+ this.context.shadowOffsetY = 0;
+ this.context.shadowBlur = 10;
+
+ // Eye top inner shadow
+ this.context.fillStyle = 'rgba(120,120,120,0.2)';
+ this.context.beginPath();
+ this.context.moveTo( el.x, el.y );
+ this.context.quadraticCurveTo( et.x, et.y, er.x, er.y );
+ this.context.quadraticCurveTo( eit.x, eit.y, el.x, el.y );
+ this.context.closePath();
+ this.context.fill();
+
+ this.context.restore();
+
+ }
+}
+
+// shim with setTimeout fallback from http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+window.requestAnimFrame = (function(){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(/* function */ callback, /* DOMElement */ element){
+ window.setTimeout(callback, 1000 / 60);
+ };
+})();
diff --git a/setup/index.html b/setup/index.html
new file mode 100644
index 0000000..64bbc12
--- /dev/null
+++ b/setup/index.html
@@ -0,0 +1,34 @@
+
+
+
+ Lazy Site
+
+
+
+ Sorry, I was too lazy to actually make this a thing
+ Go back
+
+
\ No newline at end of file