diff --git a/hug/hug.js b/hug/hug.js
new file mode 100644
index 0000000..128e049
--- /dev/null
+++ b/hug/hug.js
@@ -0,0 +1,236 @@
+var main = document.getElementById('main');
+var hearts = document.getElementById('hearts');
+
+var levels = [
+    "get a lil' hug!",
+    'get a biiig hug!',
+    'time to smile 😊',
+    'landing on planet hug'
+]
+
+var hugsies = [
+    [
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [  1, '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [  1, '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [  1,  1,  1, '', '', -1, '', '',  1, '', '',  1,  1,  1, ],
+        [  1, '', '',  1, '',  1, '', '',  1, '',  1, '', '',  1, ],
+        [  1, '', '',  1, '',  1, '', '',  2, '',  1, '', '',  1, ],
+        [  1, '', '',  1, '', '',  1,  1, '', '', '',  1,  1,  1, ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '',  1, ],
+        [ '', '', '', '', '', '', '', '', '', '',  1, '', '',  1, ],
+        [ '', '', '', '', '', '', '', '', '', '', '',  1,  1, '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+    ],
+    [
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ -1, '', '',  4, '',  6, '', '',  7, '', '',  8,  8, '', ],
+        [  3, '', '',  4, '',  7, '', '',  6, '',  8, '', '', '', ],
+        [  3, '', '',  4, '',  6, '', '',  7, '',  8, '', '', '', ],
+        [  3,  5,  5,  4, '',  7, '', '',  6, '',  9, '',  8,  8, ],
+        [  3, '', '',  4, '',  6, '', '',  7, '',  8, '', '',  8, ],
+        [  3, '', '',  4, '',  7, '', '',  6, '',  8, '', '',  8, ],
+        [  3, '', '',  4, '', '',  6,  7, '', '', '',  8,  8, '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+    ],
+    [
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', 10, '', '', '', 11, '', '', '', '', '', ],
+        [ '', '', '', '', -1, '', '', '', 11, '', '', '', '', '', ],
+        [ '', '', '', '', 10, '', '', '', 11, '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', 12, '', '', '', '', '', '', '', 18, '', '', '', ],
+        [ '', '', '', 13, '', '', '', '', '', 17, '', '', '', '', ],
+        [ '', '', '', '', 14, 13, 15, 16, 15, '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+    ],
+    [
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+        [ '', '', '', '', 22, 26, 26, 20, 20, 20, '', '', '', '', ],
+        [ '', '', '', 26, 26, 26, 26, 26, 20, 20, 20, '', '', '', ],
+        [ '', '', 25, 25, 26, 26, 26, 26, 26, 26, 20, 20, '', '', ],
+        [ '', 25, 25, 25, 26, 26, 21, 26, 19, 26, 26, 26, 26, '', ],
+        [ '', 25, 25, 26, 26, 26, 26, -1, 19, 19, 19, 26, 19, '', ],
+        [ '', 25, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, '', ],
+        [ '', 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 19, 19, '', ],
+        [ '', 23, 23, 26, 26, 24, 24, 24, 26, 24, 26, 26, 26, '', ],
+        [ '', '', 23, 26, 24, 24, 24, 24, 24, 24, 26, 24, '', '', ],
+        [ '', '', '', 26, 24, 24, 24, 26, 24, 24, 24, '', '', '', ],
+        [ '', '', '', '', 24, 24, 26, 26, 26, 24, '', '', '', '', ],
+        [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', ],
+    ],
+];
+
+var table;
+
+var session = 0;
+var kiss = 1;
+
+var cells = [];
+
+function makeCells() {
+    cells = [];
+    table = document.createElement('table');
+    main.appendChild(table);
+    for (var y = 0; y < hugsies[session].length; y++) {
+        var tr = document.createElement('tr');
+        table.appendChild(tr);
+        cells.push([]);
+        for (var x = 0; x < hugsies[session][y].length; x++) {
+            var td = document.createElement('td');
+            tr.appendChild(td);
+            cells[y][x] = td;
+        }
+    }
+}
+
+function offset(e) {
+    var top = 0;
+    var left = 0;
+    while (e) {
+        top += e.offsetTop;
+        left += e.offsetLeft;
+        e = e.offsetParent;
+    }
+    return { top: top, left: left };
+}
+
+function max() {
+    return Math.max.apply(Math, hugsies[session].map(function (row) { return Math.max.apply(Math, row); }));
+}
+
+function mini(session) {
+    for (var y = 0; y < hugsies[session].length; y++) {
+        for (var x = 0; x < hugsies[session][y].length; x++) {
+            if (hugsies[session][y][x] == -1) {
+                return { y: y, x: x };
+            }
+        }
+    }
+}
+
+function nextSession() {
+    cuddle();
+    session++;
+    if (session >= hugsies.length) {
+        session = 0;
+        kiss = 1;
+    };
+
+    window.setTimeout(function() {
+        var futureMini =  mini(session);
+        var destOffset = offset(cells[futureMini.y][futureMini.x]);
+        var currentOffset = offset(table);
+
+        var scale = 'scale(calc(1 / ' + Math.max(cells.length, cells[0].length) + '))';
+
+        table.style.transform = 'translate('+(destOffset.left - currentOffset.left)+'px,'+(destOffset.top-currentOffset.top)+'px) ' + scale;
+        
+        window.setTimeout(function() {
+            var miniTable = table;
+            makeCells();
+            cuddle();
+
+            for (var y = 0; y < cells.length; y++) {
+                for (var x = 0; x < cells[y].length; x++) {
+                    cells[y][x].style.opacity = 0;
+                    window.setTimeout((function(c) { return function() { c.style.opacity = 1; } })(cells[y][x]), Math.sqrt(y*y + x*x) * 100)
+                }
+            }
+
+            var miniyx = mini(session);
+
+            cells[miniyx.y][miniyx.x].innerText = '';
+            cells[miniyx.y][miniyx.x].appendChild(miniTable);
+            miniTable.style.transform = scale;
+            miniTable.style.position = 'absolute';
+            var nested = miniTable.getElementsByTagName('table');
+            for (var i = 0; i < nested.length; i++) {
+                if (nested[i] != miniTable) {
+                    nested[i].parentElement.innerHTML = '🤗';
+                }
+            }
+            cells[miniyx.y][miniyx.x].className = 'on mini';
+            cells[miniyx.y][miniyx.x].style.opacity = 1;
+        }, 3000);
+    }, 1000);
+}
+
+var pauseKiss = 0;
+var mouse = { x: 0, y: 0 }
+var timeout = false;
+var kisses = 0;
+
+function muah() { kiss++; if (kiss >= max()) { pauseKiss = Date.now() + 3000; window.clearTimeout(timeout); nextSession(); } else { cuddle(); } }
+function blowKiss(e) {
+    if (Date.now() > pauseKiss) {
+        var heart = document.createElement('div');
+        hearts.appendChild(heart);
+        var emojis = ['❤️', '♥️', '💗', '<3', '💖', '💛', '💙', '💜', '💚', '💞', '💝', '💌', '💕'];
+        heart.innerText = emojis[Math.floor(Math.random() * emojis.length)];
+        heart.style.position = 'absolute';
+        heart.style.top = mouse.y + 'px';
+        heart.style.left = mouse.x + 'px';
+
+        if (timeout) { window.clearInterval (timeout) };
+        kisses++;
+        if (kisses > 5) {
+            pauseKiss = Date.now() + 4000;
+            kisses = 0;
+        }
+        timeout = window.setTimeout(blowKiss, 300 + Math.random() * 700)
+    } else {
+        timeout = window.setTimeout(blowKiss, pauseKiss - Date.now() + 100);
+    }
+}
+function blowKissEnter(e) { blowKissMove(e); blowKiss(); }
+function blowKissLeave() { if (timeout) { window.clearTimeout(timeout) }; }
+function blowKissMove(e) { mouse = { x: e.clientX, y: e.clientY }; }
+
+
+function cuddle() {
+    document.getElementById('level').innerText = levels[session];
+    for (var y = 0; y < hugsies[session].length; y++) {
+        for (var x = 0; x < hugsies[session][y].length; x++) {
+            var td = cells[y][x];
+            var classy = [];
+            var emoji = '🤗';
+            if (hugsies[session][y][x] == 26) { emoji = '💙'; }
+            if (hugsies[session][y][x] == 0 || hugsies[session][y][x] > kiss) {
+                classy.push('off');
+            } else {
+                classy.push('on');
+            }
+            if (hugsies[session][y][x] == kiss + 1) {
+                classy.push('next');
+                emoji = '😘';
+                td.addEventListener('click', muah);
+                td.addEventListener('mousemove', blowKissMove);
+                td.addEventListener('mouseleave', blowKissLeave);
+                td.addEventListener('mouseenter', blowKissEnter);
+            } else {
+                td.removeEventListener('click', muah);
+                td.removeEventListener('mousemove', blowKissMove);
+                td.removeEventListener('mouseleave', blowKissLeave);
+                td.removeEventListener('mouseenter', blowKissEnter);
+            }
+            if (td.getElementsByTagName('table').length == 0) {
+                td.className = classy.join(' ');
+                td.innerText = emoji;
+            }
+        }
+    }
+}
+
+makeCells();
+cuddle();
\ No newline at end of file
diff --git a/hug/index.html b/hug/index.html
new file mode 100644
index 0000000..e8e42b9
--- /dev/null
+++ b/hug/index.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+        <title>Hug?</title>
+        <style>
+            html, body { height: 100%; padding: 0; margin: 0; }
+            body { font-size: 2em; display: grid; justify-items: center; align-content: center; }
+            #main { }
+            table { transition: transform 3s cubic-bezier(1,0,0.5,1); transform-origin: top left; }
+            table, tr, td { border: none; padding: 0; }
+            td { position: relative; }
+            td { cursor: default; transition: filter 1s ease-in-out, opacity 1s ease-in-out; filter: grayscale(100%) brightness(50%) blur(0.3em); }
+            td.on { filter: grayscale(0%) brightness(100%) blur(0em); }
+            td.mini { vertical-align: top; }
+            td.next { cursor: pointer; filter: grayscale(100%) brightness(100%) blur(0.3em); }
+            td.next:hover { cursor: pointer; filter: grayscale(0%) brightness(100%) blur(0em); }
+            #hearts * { color: pink; font-weight: bold; font-family: monospace; opacity: 0; }
+            #hearts :nth-child(3n+0) { animation: flutter0 3s linear; }
+            #hearts :nth-child(3n+1) { animation: flutter1 3.2s linear; }
+            #hearts :nth-child(3n+2) { animation: flutter2 3.3s linear; }
+            #hearts { pointer-events: none; }
+            @keyframes flutter0 {
+                0%   { opacity: 0.9;   transform: translate( 0.00em,   0.00em) scale(1.00); }
+                10%  { opacity: 0.9;   transform: translate( 0.17em,  -1.36em) scale(1.46); }
+                20%  { opacity: 0.9;   transform: translate( 0.36em,  -2.64em) scale(1.55); }
+                30%  { opacity: 0.9;   transform: translate( 0.08em,  -3.84em) scale(1.09); }
+                40%  { opacity: 0.9;   transform: translate(-0.61em,  -4.96em) scale(0.47); }
+                50%  { opacity: 0.9;   transform: translate(-0.96em,  -6.00em) scale(0.28); }
+                60%  { opacity: 0.9;   transform: translate(-0.34em,  -6.96em) scale(0.78); }
+                70%  { opacity: 0.9;   transform: translate( 0.92em,  -7.84em) scale(1.56); }
+                80%  { opacity: 0.9;   transform: translate( 1.58em,  -8.64em) scale(1.89); }
+                90%  { opacity: 0.6; transform: translate( 0.74em,  -9.36em) scale(1.39); }
+                100% { opacity: 0;   transform: translate(-1.09em, -10.00em) scale(0); }
+            }
+            @keyframes flutter1 {
+                0%   { opacity: 0.9;   transform: translate( 0.00em,   0.00em) scale(1.00); }
+                10%  { opacity: 0.9;   transform: translate(-0.15em,  -1.64em) scale(1.46); }
+                20%  { opacity: 0.9;   transform: translate(-0.38em,  -3.16em) scale(1.55); }
+                30%  { opacity: 0.9;   transform: translate(-0.17em,  -4.56em) scale(1.09); }
+                40%  { opacity: 0.9;   transform: translate( 0.53em,  -5.84em) scale(0.47); }
+                50%  { opacity: 0.9;   transform: translate( 0.99em,  -7.00em) scale(0.28); }
+                60%  { opacity: 0.9;   transform: translate( 0.49em,  -8.04em) scale(0.78); }
+                70%  { opacity: 0.9;   transform: translate(-0.76em,  -8.96em) scale(1.56); }
+                80%  { opacity: 0.9;   transform: translate(-1.60em,  -9.76em) scale(1.89); }
+                90%  { opacity: 0.6; transform: translate(-0.97em, -10.44em) scale(1.39); }
+                100% { opacity: 0;   transform: translate( 0.84em, -11.00em) scale(0); }
+            }
+            @keyframes flutter2 {
+                0%   { opacity: 0.9;   transform: translate( 0.00em,  -0.00em) scale(1.00); }
+                10%  { opacity: 0.9;   transform: translate( 0.13em,  -1.17em) scale(1.46); }
+                20%  { opacity: 0.9;   transform: translate( 0.40em,  -2.28em) scale(1.55); }
+                30%  { opacity: 0.9;   transform: translate( 0.25em,  -3.33em) scale(1.09); }
+                40%  { opacity: 0.9;   transform: translate(-0.44em,  -4.32em) scale(0.47); }
+                50%  { opacity: 0.9;   transform: translate(-1.00em,  -5.25em) scale(0.28); }
+                60%  { opacity: 0.9;   transform: translate(-0.64em,  -6.12em) scale(0.78); }
+                70%  { opacity: 0.9;   transform: translate( 0.59em,  -6.93em) scale(1.56); }
+                80%  { opacity: 0.9;   transform: translate( 1.58em,  -7.68em) scale(1.89); }
+                90%  { opacity: 0.6; transform: translate( 1.17em,  -8.37em) scale(1.39); }
+                100% { opacity: 0;   transform: translate(-0.58em,  -9.00em) scale(0); }
+            }
+            #level {
+                font-size: 1.5rem;
+                font-family: monospace;
+                color: pink;
+                text-align: center;
+            }
+        </style>
+    </head>
+    <body>
+        <h1 id="level"></h1>
+        <div id="main"></div>
+        <div id="hearts"></div>
+        <script src="hug.js"></script>
+    </body>
+</html>
\ No newline at end of file