const playSound = await (async function() {
   const src1 = " ";
   const src2 = " ";

   const audioCtx = new AudioContext();
   
   function decode(base64Src) {
   var binaryString = atob(base64Src);
   var arrayBuffer = new ArrayBuffer(binaryString.length);
   var bufferView = new Uint8Array(arrayBuffer);
   for (var i = 0; i < binaryString.length; i++) {
     bufferView[i] = binaryString.charCodeAt(i);
   }
     return new Promise((res, rej)=>
     audioCtx.decodeAudioData(arrayBuffer, function(audioBuffer) {
       // Use the audioBuffer for further processing or playback
       res(audioBuffer);
     }, function(error) {
       rej(error);
     }));
   }
   function play(buffer, detune) {
     if(isNaN(detune)) return;
     const buffSrc = audioCtx.createBufferSource();
     buffSrc.buffer = buffer;
     buffSrc.connect(audioCtx.destination);
     buffSrc.detune.value = detune * 100;
     buffSrc.start();
   }
   function playMultiple(buffer, detuneCollection) {
     if(Array.isArray(detuneCollection)) {
       for(detune of detuneCollection) {
         play(buffer, detune);
       }
     }
     else play(buffer, detuneCollection);
   }
   const buff1 = await decode(src1);
   const buff2 = await decode(src2);
   return function(high, low) {
     playMultiple(buff1, high);
     playMultiple(buff2, low);
   }
   })();
   //usage: playSound(high: [arr of tune], low: [arr of tune]);

//the pxxl.js
function foldl(a, b, c) { for (var d = 0; d < c.length; ++d)b = a(b, c[d]); return b } function ParseState(a, b) { return this.input = a, this.index = b || 0, this.length = a.length - this.index, this.cache = {}, this } function ps(a) { return new ParseState(a) } function make_result(a, b, c) { return { remaining: a, matched: b, ast: c } } function token(a) { var b = parser_id++; return function (c) { var d = c, e = d.getCached(b); if (e) return e; var f = c.length >= a.length && c.substring(0, a.length) == a; return f ? e = { remaining: c.from(a.length), matched: a, ast: a } : e = !1, d.putCached(b, e), e } } function ch(a) { var b = parser_id++; return function (d) { var e = d, f = e.getCached(b); if (f) return f; var g = d.length >= 1 && d.at(0) == a; return g ? f = { remaining: d.from(1), matched: a, ast: a } : f = !1, e.putCached(b, f), f } } function range(a, b) { var c = parser_id++; return function (d) { var e = d, f = e.getCached(c); if (f) return f; if (d.length < 1) f = !1; else { var g = d.at(0); g >= a && g <= b ? f = { remaining: d.from(1), matched: g, ast: g } : f = !1 } return e.putCached(c, f), f } } function toParser(a) { return typeof a == "string" ? token(a) : a } function whitespace(a) { var a = toParser(a), b = parser_id++; return function (c) { var d = c, e = d.getCached(b); return e ? e : (e = a(c.trimLeft()), d.putCached(b, e), e) } } function action(a, b) { var a = toParser(a), c = parser_id++; return function (d) { var e = d, g = e.getCached(c); if (g) return g; var h = a(d); return h ? (h.ast = b(h.ast), g = h) : g = !1, e.putCached(c, g), g } } function join_action(a, b) { return action(a, function (a) { return a.join(b) }) } function left_factor(a) { return foldl(function (a, b) { return [a, b] }, a[0], a[1]) } function left_factor_action(a) { return action(a, left_factor) } function negate(a) { var a = toParser(a), b = parser_id++; return function (c) { var d = c, e = d.getCached(b); if (e) return e; if (c.length >= 1) { var f = a(c); f ? e = !1 : e = make_result(c.from(1), c.at(0), c.at(0)) } else e = !1; return d.putCached(b, e), e } } function end_p(a) { return a.length == 0 ? make_result(a, undefined, undefined) : !1 } function nothing_p(a) { return !1 } function sequence() { var a = []; for (var b = 0; b < arguments.length; ++b)a.push(toParser(arguments[b])); var c = parser_id++; return function (b) { var d = b, e = d.getCached(c); if (e) return e; var f = [], g = "", h; for (h = 0; h < a.length; ++h) { var i = a[h], j = i(b); if (j) b = j.remaining, j.ast != undefined && (f.push(j.ast), g = g + j.matched); else break } return h == a.length ? e = make_result(b, g, f) : e = !1, d.putCached(c, e), e } } function wsequence() { var a = []; for (var b = 0; b < arguments.length; ++b)a.push(whitespace(toParser(arguments[b]))); return sequence.apply(null, a) } function choice() { var a = []; for (var b = 0; b < arguments.length; ++b)a.push(toParser(arguments[b])); var c = parser_id++; return function (b) { var d = b, e = d.getCached(c); if (e) return e; var f; for (f = 0; f < a.length; ++f) { var g = a[f], h = g(b); if (h) break } return f == a.length ? e = !1 : e = h, d.putCached(c, e), e } } function butnot(a, b) { var a = toParser(a), b = toParser(b), c = parser_id++; return function (d) { var e = d, f = e.getCached(c); if (f) return f; var g = b(d); if (!g) f = a(d); else { var h = a(d); h ? h.matched.length > g.matched.length ? f = h : f = !1 : f = !1 } return e.putCached(c, f), f } } function difference(a, b) { var a = toParser(a), b = toParser(b), c = parser_id++; return function (d) { var e = d, f = e.getCached(c); if (f) return f; var g = b(d); if (!g) f = a(d); else { var h = a(d); h.matched.length >= g.matched.length ? f = g : f = h } return e.putCached(c, f), f } } function xor(a, b) { var a = toParser(a), b = toParser(b), c = parser_id++; return function (d) { var e = d, f = e.getCached(c); if (f) return f; var g = a(d), h = b(d); return g && h ? f = !1 : f = g || h, e.putCached(c, f), f } } function repeat0(a) { var a = toParser(a), b = parser_id++; return function (c) { var d = c, e = d.getCached(b); if (e) return e; var f = [], g = "", h; while (h = a(c)) { f.push(h.ast), g = g + h.matched; if (h.remaining.index == c.index) break; c = h.remaining } return e = make_result(c, g, f), d.putCached(b, e), e } } function repeat1(a) { var a = toParser(a), b = parser_id++; return function (c) { var d = c, e = d.getCached(b); if (e) return e; var f = [], g = "", h = a(c); if (!h) e = !1; else { while (h) { f.push(h.ast), g = g + h.matched; if (h.remaining.index == c.index) break; c = h.remaining, h = a(c) } e = make_result(c, g, f) } return d.putCached(b, e), e } } function optional(a) { var a = toParser(a), b = parser_id++; return function (c) { var d = c, e = d.getCached(b); if (e) return e; var f = a(c); return e = f || make_result(c, "", !1), d.putCached(b, e), e } } function expect(a) { return action(a, function (a) { return undefined }) } function chain(a, b, c) { var a = toParser(a); return action(sequence(a, repeat0(action(sequence(b, a), c))), function (a) { return [a[0]].concat(a[1]) }) } function chainl(a, b) { var a = toParser(a); return action(sequence(a, repeat0(sequence(b, a))), function (a) { return foldl(function (a, b) { return b[0](a, b[1]) }, a[0], a[1]) }) } function list(a, b) { return chain(a, b, function (a) { return a[1] }) } function wlist() { var a = []; for (var b = 0; b < arguments.length; ++b)a.push(whitespace(arguments[b])); return list.apply(null, a) } function epsilon_p(a) { return make_result(a, "", undefined) } function semantic(a) { var b = parser_id++; return function (c) { var d = c, e = d.getCached(b); return e ? e : (e = a() ? make_result(c, "", undefined) : !1, d.putCached(b, e), e) } } function and(a) { var a = toParser(a), b = parser_id++; return function (c) { var d = c, e = d.getCached(b); if (e) return e; var f = a(c); return e = f ? make_result(c, "", undefined) : !1, d.putCached(b, e), e } } function not(a) { var a = toParser(a), b = parser_id++; return function (c) { var d = c, e = d.getCached(b); return e ? e : (e = a(c) ? !1 : make_result(c, "", undefined), d.putCached(b, e), e) } } function pxxl(a, b, c) { Pxxl.LoadFont(a, function (a) { var d = a.getPixels(b); c(d, a) }) } var memoize = !0; ParseState.prototype.from = function (a) { var b = new ParseState(this.input, this.index + a); return b.cache = this.cache, b.length = this.length - a, b }, ParseState.prototype.substring = function (a, b) { return this.input.substring(a + this.index, (b || this.length) + this.index) }, ParseState.prototype.trimLeft = function () { var a = this.substring(0), b = a.match(/^\s+/); return b ? this.from(b[0].length) : this }, ParseState.prototype.at = function (a) { return this.input.charAt(this.index + a) }, ParseState.prototype.toString = function () { return 'PS"' + this.substring(0) + '"' }, ParseState.prototype.getCached = function (a) { if (!memoize) return !1; var b = this.cache[a]; return b ? b[this.index] : !1 }, ParseState.prototype.putCached = function (a, b) { if (!memoize) return !1; var c = this.cache[a]; c ? c[this.index] = b : (c = this.cache[a] = {}, c[this.index] = b) }; var parser_id = 0, Pxxl = {}; Pxxl.Font = function (a, b, c, d) { this.version = a, this.comments = b, this.properties = c, this.glyphs = d }, Pxxl.Font.prototype = { size: function () { return this.SIZE[0] }, getGlyph: function (a) { var b = a.charCodeAt(0); return this.glyphs[b] }, defaultWidth: function () { return this.FONTBOUNDINGBOX[0] }, defaultHeight: function () { return this.FONTBOUNDINGBOX[1] }, bit: function (a, b, c) { var d = ~~(c / 8); if (d < 0 || d > a.length - 1) return !1; var e = a.charCodeAt(d), f = this.glyphs[e]; return f ? f.bit(b, c % 8) : !1 }, getPixels: function (a) { var b = this.ctx, c = this.FONTBOUNDINGBOX[0], d = []; for (var e = 0; e < a.length; e++) { var f = a.charCodeAt(e), g = this.glyphs[f], h = g.bitmap, i = e * c, j = this.defaultHeight() - g.height(); for (var k = 0; k < h.length; k++) { var l = h[k]; for (var m = 0; m < l.length; m++) { var n = l[m], o = m * 8, p = 256; while (p >>>= 1) { if (n & p) { var q = i + o, r = j + k; d.push({ x: q, y: r, row: k, column: o }) } o++ } } } } return d } }, Pxxl.Glyph = function (a, b) { this.name = a, this.bitmap = b }, Pxxl.Glyph.prototype = { set: function (a, b, c) { var d = 1 << this.width() - a - 1, e = ~~(d / 256); d %= (e + 1) * 256, c ? this.bitmap[b][e] |= d : this.bitmap[b][e] &= ~d }, get: function (a, b) { var c = 1 << this.width() - a - 1, d = ~~(c / 256); c %= (d + 1) * 256; var e = this.bitmap[b][d] & c; return !!e }, width: function () { return this.BBX[0] }, height: function () { return this.BBX[1] }, toString: function () { var a = ""; for (var b = 0; b < this.bitmap.length; b++) { for (var c = 0; c < this.width(); c++)a += this.get(c, b) ? "*" : " "; a += "/n" } return a } }, function () { function by(a) { var b = a[1], c = a[0].concat(a[2]), d = a[3], e = bB(a[4]), f = new Pxxl.Font(b, c, d, e); return bA(f, d) } function bz(a) { var b = a[0], c = a[1], d = a[2], e = new Pxxl.Glyph(b, d); return e = bA(e, c), { name: e.ENCODING, value: e } } function bA(a, b) { for (var c = 0; c < b.length; c++) { var d = b[c]; a[d.name] = d.value } return a } function bB(a) { var b = {}; for (var c = 0; c < a.length; c++) { var d = a[c]; b[d.name] = d.value } return b } function bC(a) { var b = a[1], c = a[0]; if (c == "ENCODING" || c == "CHARS") b = b[0]; return { name: c, value: b } } function bD(a) { return { name: a[0], value: a[2] } } function bE(a) { return join_action(a, "") } function bF(a, b) { return action(b, function (b) { return b[a] }) } function bG(a, b) { var c = Pxxl.trace, d = Pxxl.traceall; return c ? function (c) { var e = a(c); if (!e.ast) { var f = c.input.substring(0, c.index), g = f.split("\n"); console.error(b, "failed at line", g.length, c) } return e.ast && d && console.log(b, "matches", e.matched, "\nAST:", e.ast), e } : a } function bH(a) { var b = a.split("\n"); for (var c = b.length - 1; c >= 0; c--) { var d = bI(b[c]); d == "" ? b.splice(c, 1) : b[c] = d } return b.join("\n") } function bI(a) { return a.replace(/^\s+/, "") } function bJ(a, b, c) { Pxxl.trace = b, Pxxl.traceall = c, a = bH(a); var d = ps(a), e = +(new Date), f = bx(d), g = +(new Date) - e; if (f.ast) return f.ast; throw new Error("Unable to parse font!") } var a = ch("!"), b = ch("@"), c = ch("#"), d = ch("$"), e = ch("%"), f = ch("^"), g = ch("&"), h = ch("*"), i = ch("("), j = ch(")"), k = ch("-"), l = ch("_"), m = ch("+"), n = ch("="), o = ch("{"), p = ch("}"), q = ch("["), r = ch("]"), s = ch(":"), t = ch(";"), u = ch("'"), v = ch('"'), w = ch("|"), x = ch("\\"), y = ch("~"), z = ch("`"), A = ch(","), B = ch("."), C = ch("<"), D = ch(">"), E = ch("?"), F = ch("/"), G = choice(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F), H = range("0", "9"), I = range("a", "z"), J = range("A", "Z"), K = ch("\n"), L = ch(" "), M = ch("\t"), N = choice(I, J), O = choice(N, H), P = choice(O, G), Q = choice(P, L), R = bE(repeat1(L)), S = bE(repeat1(Q)), T = sequence(repeat0(L), K), U = bF(1, sequence(v, bE(repeat1(butnot(Q, v))), v)), V = choice(range("a", "f"), range("A", "F"), H), W = action(bE(sequence(V, V)), function (a) { return parseInt(a, 16) }), X = repeat1(W), Y = bE(repeat1(H)), Z = bE(sequence(k, Y)), $ = action(choice(Y, Z), parseInt), _ = bE(repeat1(choice(N, l))), ba = action(sequence(_, repeat1(bF(1, sequence(R, $)))), bC), bb = action(sequence(_, R, U), bD), bc = action(sequence(_, R, bE(repeat1(P))), bD), bd = token("ENDPROPERTIES"), be = bG(choice(ba, bb, bc, bd), "prop"), bf = bF(0, sequence(be, T)), bg = bF(0, sequence(X, T)), bh = token("BITMAP"), bi = sequence(bh, T), bj = bG(bF(1, sequence(bi, repeat0(bg))), "bitmap"), bk = token("STARTCHAR"), bl = token("ENDCHAR"), bm = bG(bF(2, sequence(bk, L, S, T)), "glyphstart"), bn = sequence(bl, T), bo = bG(action(sequence(bm, repeat0(bf), bj, bn), bz), "glyph"), bp = token("STARTFONT"), bq = token("ENDFONT"), br = bE(sequence(Y, B, Y)), bs = bG(bF(2, sequence(bp, R, br, T)), "fontstart"), bt = bG(sequence(bq, optional(T)), "fontend"), bu = token("COMMENT"), bv = bF(2, sequence(bu, optional(L), optional(S))), bw = bG(bF(0, sequence(bv, T)), "comment"), bx = action(sequence(repeat0(bw), bs, repeat0(bw), repeat0(butnot(bf, bm)), repeat0(bo), bt), by); Pxxl.Font.ParseBDF = bJ }(), Pxxl.Glyph.ParseJSON = function (a) { var b = new Pxxl.Glyph(a.name, a.bitmap); for (var c in a) a.hasOwnProperty(c) && (b[c] = a[c]); return b }, Pxxl.Font.ParseJSON = function (a) { var b = new Pxxl.Font(a.version, a.comments, a.properties, {}); for (var c in a) a.hasOwnProperty(c) && c != "glyphs" && (b[c] = a[c]); b.glyphs = {}; for (var d in a.glyphs) a.glyphs.hasOwnProperty(d) && (b.glyphs[d] = Pxxl.Glyph.ParseJSON(a.glyphs[d])); return b }, function () { function a(a, b, d) { var e = c(); if (!e) return; var f = d ? "POST" : "GET"; e.open(f, a, !0), d && e.setRequestHeader("Content-type", "application/x-www-form-urlencoded"), e.onreadystatechange = function () { if (e.readyState != 4) return; if (e.status != 200 && e.status != 304) return; b(e) }; if (e.readyState == 4) return; e.send(d) } function c() { var a = !1; for (var c = 0; c < b.length; c++) { try { a = b[c]() } catch (d) { continue } break } return a } function d(b, c) { a(b, function (a) { c(Pxxl.Font.ParseBDF(a.responseText)) }) } function e(a) { var b = {}; return function (c, d) { var e = b[c]; return typeof e != "undefined" ? d(e) : a(c, function (a) { return b[c] = a, d(a) }) } } var b = [function () { return new XMLHttpRequest }, function () { return new ActiveXObject("Msxml2.XMLHTTP") }, function () { return new ActiveXObject("Msxml3.XMLHTTP") }, function () { return new ActiveXObject("Microsoft.XMLHTTP") }]; Pxxl.LoadFont = e(d) }();


//----------
function loadFont(url) {
   return new Promise(res => {
      Pxxl.LoadFont(url,
         f => res(f));
   });
}
const font1 = await loadFont("https://raw.githubusercontent.com/remcoder/Pxxl.js/master/fonts/c64.bdf");
const font2 = await loadFont("https://raw.githubusercontent.com/remcoder/Pxxl.js/master/fonts/MonteCarloBold.bdf");
const font3 = await loadFont("https://raw.githubusercontent.com/remcoder/Pxxl.js/master/fonts/MonteCarloMedium.bdf");

const graph = document.getElementsByClassName("js-calendar-graph")[0].getElementsByTagName("table")[0];
//github changed website tags :skull:
const boxes = (function () {
   const boxesRaw = [...graph.getElementsByTagName("tr")]
      .map(e => [...e.getElementsByTagName("td")].filter(el => el.classList.contains("ContributionCalendar-day")))
      .filter(e => e.length > 0);
   let bx = [];
   for (let i = 0; i < boxesRaw[0].length; i++) {
      bx[i] = boxesRaw.map(a => a[i]);
   }
   return bx;
})();

//========================================
// predefined data
//---------------------------------------
const gitPushForce = [{ "x": 24, "y": 0, "level": 4 }, { "x": 38, "y": 0, "level": 2 }, { "x": 39, "y": 0, "level": 4 }, { "x": 40, "y": 0, "level": 4 }, { "x": 4, "y": 1, "level": 4 }, { "x": 7, "y": 1, "level": 4 }, { "x": 21, "y": 1, "level": 2 }, { "x": 22, "y": 1, "level": 4 }, { "x": 24, "y": 1, "level": 4 }, { "x": 38, "y": 1, "level": 4 }, { "x": 53, "y": 1, "level": 3 }, { "x": 54, "y": 1, "level": 4 }, { "x": 55, "y": 1, "level": 2 }, { "x": 59, "y": 1, "level": 4 }, { "x": 60, "y": 1, "level": 4 }, { "x": 62, "y": 1, "level": 4 }, { "x": 63, "y": 1, "level": 4 }, { "x": 0, "y": 2, "level": 2 }, { "x": 1, "y": 2, "level": 4 }, { "x": 2, "y": 2, "level": 4 }, { "x": 6, "y": 2, "level": 4 }, { "x": 7, "y": 2, "level": 4 }, { "x": 8, "y": 2, "level": 4 }, { "x": 13, "y": 2, "level": 4 }, { "x": 14, "y": 2, "level": 4 }, { "x": 15, "y": 2, "level": 2 }, { "x": 17, "y": 2, "level": 4 }, { "x": 19, "y": 2, "level": 4 }, { "x": 21, "y": 2, "level": 4 }, { "x": 24, "y": 2, "level": 4 }, { "x": 25, "y": 2, "level": 4 }, { "x": 26, "y": 2, "level": 2 }, { "x": 31, "y": 2, "level": 4 }, { "x": 32, "y": 2, "level": 4 }, { "x": 34, "y": 2, "level": 4 }, { "x": 35, "y": 2, "level": 4 }, { "x": 37, "y": 2, "level": 4 }, { "x": 38, "y": 2, "level": 4 }, { "x": 39, "y": 2, "level": 4 }, { "x": 41, "y": 2, "level": 2 }, { "x": 42, "y": 2, "level": 4 }, { "x": 43, "y": 2, "level": 3 }, { "x": 45, "y": 2, "level": 3 }, { "x": 46, "y": 2, "level": 4 }, { "x": 47, "y": 2, "level": 4 }, { "x": 49, "y": 2, "level": 2 }, { "x": 50, "y": 2, "level": 4 }, { "x": 53, "y": 2, "level": 4 }, { "x": 55, "y": 2, "level": 4 }, { "x": 58, "y": 2, "level": 4 }, { "x": 59, "y": 2, "level": 4 }, { "x": 60, "y": 2, "level": 4 }, { "x": 61, "y": 2, "level": 4 }, { "x": 62, "y": 2, "level": 4 }, { "x": 63, "y": 2, "level": 4 }, { "x": 64, "y": 2, "level": 4 }, { "x": 0, "y": 3, "level": 4 }, { "x": 2, "y": 3, "level": 4 }, { "x": 4, "y": 3, "level": 4 }, { "x": 7, "y": 3, "level": 4 }, { "x": 13, "y": 3, "level": 4 }, { "x": 15, "y": 3, "level": 4 }, { "x": 17, "y": 3, "level": 4 }, { "x": 19, "y": 3, "level": 4 }, { "x": 22, "y": 3, "level": 4 }, { "x": 24, "y": 3, "level": 4 }, { "x": 26, "y": 3, "level": 4 }, { "x": 38, "y": 3, "level": 4 }, { "x": 41, "y": 3, "level": 4 }, { "x": 43, "y": 3, "level": 4 }, { "x": 45, "y": 3, "level": 4 }, { "x": 49, "y": 3, "level": 4 }, { "x": 53, "y": 3, "level": 3 }, { "x": 54, "y": 3, "level": 4 }, { "x": 58, "y": 3, "level": 4 }, { "x": 59, "y": 3, "level": 4 }, { "x": 60, "y": 3, "level": 4 }, { "x": 61, "y": 3, "level": 4 }, { "x": 62, "y": 3, "level": 4 }, { "x": 63, "y": 3, "level": 4 }, { "x": 64, "y": 3, "level": 4 }, { "x": 0, "y": 4, "level": 4 }, { "x": 1, "y": 4, "level": 4 }, { "x": 2, "y": 4, "level": 4 }, { "x": 4, "y": 4, "level": 4 }, { "x": 7, "y": 4, "level": 3 }, { "x": 8, "y": 4, "level": 4 }, { "x": 13, "y": 4, "level": 4 }, { "x": 14, "y": 4, "level": 4 }, { "x": 15, "y": 4, "level": 3 }, { "x": 17, "y": 4, "level": 2 }, { "x": 18, "y": 4, "level": 4 }, { "x": 19, "y": 4, "level": 4 }, { "x": 21, "y": 4, "level": 4 }, { "x": 22, "y": 4, "level": 3 }, { "x": 24, "y": 4, "level": 4 }, { "x": 26, "y": 4, "level": 4 }, { "x": 38, "y": 4, "level": 4 }, { "x": 41, "y": 4, "level": 3 }, { "x": 42, "y": 4, "level": 4 }, { "x": 43, "y": 4, "level": 2 }, { "x": 45, "y": 4, "level": 4 }, { "x": 49, "y": 4, "level": 3 }, { "x": 50, "y": 4, "level": 4 }, { "x": 51, "y": 4, "level": 4 }, { "x": 53, "y": 4, "level": 2 }, { "x": 54, "y": 4, "level": 4 }, { "x": 55, "y": 4, "level": 4 }, { "x": 59, "y": 4, "level": 4 }, { "x": 60, "y": 4, "level": 4 }, { "x": 61, "y": 4, "level": 4 }, { "x": 62, "y": 4, "level": 4 }, { "x": 63, "y": 4, "level": 4 }, { "x": 2, "y": 5, "level": 4 }, { "x": 13, "y": 5, "level": 4 }, { "x": 60, "y": 5, "level": 4 }, { "x": 61, "y": 5, "level": 4 }, { "x": 62, "y": 5, "level": 4 }, { "x": 0, "y": 6, "level": 4 }, { "x": 1, "y": 6, "level": 4 }, { "x": 2, "y": 6, "level": 2 }, { "x": 13, "y": 6, "level": 4 }, { "x": 61, "y": 6, "level": 4 }];
const octoCatData = [{ "level": 2, "x": 7, "y": 0 }, { "level": 3, "x": 8, "y": 0 }, { "level": 3, "x": 17, "y": 0 }, { "level": 2, "x": 18, "y": 0 }, { "level": 3, "x": 7, "y": 1 }, { "level": 4, "x": 8, "y": 1 }, { "level": 4, "x": 9, "y": 1 }, { "level": 3, "x": 10, "y": 1 }, { "level": 3, "x": 15, "y": 1 }, { "level": 4, "x": 16, "y": 1 }, { "level": 4, "x": 17, "y": 1 }, { "level": 3, "x": 18, "y": 1 }, { "level": 3, "x": 7, "y": 2 }, { "level": 4, "x": 8, "y": 2 }, { "level": 4, "x": 9, "y": 2 }, { "level": 4, "x": 10, "y": 2 }, { "level": 4, "x": 11, "y": 2 }, { "level": 4, "x": 12, "y": 2 }, { "level": 4, "x": 13, "y": 2 }, { "level": 4, "x": 14, "y": 2 }, { "level": 4, "x": 15, "y": 2 }, { "level": 4, "x": 16, "y": 2 }, { "level": 4, "x": 17, "y": 2 }, { "level": 3, "x": 18, "y": 2 }, { "level": 2, "x": 7, "y": 3 }, { "level": 4, "x": 8, "y": 3 }, { "level": 4, "x": 9, "y": 3 }, { "level": 4, "x": 10, "y": 3 }, { "level": 4, "x": 11, "y": 3 }, { "level": 4, "x": 12, "y": 3 }, { "level": 4, "x": 13, "y": 3 }, { "level": 4, "x": 14, "y": 3 }, { "level": 4, "x": 15, "y": 3 }, { "level": 4, "x": 16, "y": 3 }, { "level": 4, "x": 17, "y": 3 }, { "level": 2, "x": 18, "y": 3 }, { "level": 2, "x": 6, "y": 4 }, { "level": 4, "x": 7, "y": 4 }, { "level": 4, "x": 8, "y": 4 }, { "level": 4, "x": 9, "y": 4 }, { "level": 4, "x": 10, "y": 4 }, { "level": 4, "x": 11, "y": 4 }, { "level": 4, "x": 12, "y": 4 }, { "level": 4, "x": 13, "y": 4 }, { "level": 4, "x": 14, "y": 4 }, { "level": 4, "x": 15, "y": 4 }, { "level": 4, "x": 16, "y": 4 }, { "level": 4, "x": 17, "y": 4 }, { "level": 4, "x": 18, "y": 4 }, { "level": 2, "x": 19, "y": 4 }, { "level": 4, "x": 6, "y": 5 }, { "level": 4, "x": 7, "y": 5 }, { "level": 4, "x": 8, "y": 5 }, { "level": 3, "x": 9, "y": 5 }, { "level": 3, "x": 17, "y": 5 }, { "level": 4, "x": 18, "y": 5 }, { "level": 4, "x": 19, "y": 5 }, { "level": 2, "x": 3, "y": 6 }, { "level": 3, "x": 4, "y": 6 }, { "level": 3, "x": 5, "y": 6 }, { "level": 4, "x": 6, "y": 6 }, { "level": 4, "x": 7, "y": 6 }, { "level": 4, "x": 8, "y": 6 }, { "level": 4, "x": 11, "y": 6 }, { "level": 4, "x": 15, "y": 6 }, { "level": 4, "x": 18, "y": 6 }, { "level": 4, "x": 19, "y": 6 }, { "level": 3, "x": 20, "y": 6 }, { "level": 2, "x": 21, "y": 6 }, { "level": 4, "x": 6, "y": 7 }, { "level": 4, "x": 7, "y": 7 }, { "level": 4, "x": 8, "y": 7 }, { "level": 4, "x": 11, "y": 7 }, { "level": 4, "x": 15, "y": 7 }, { "level": 4, "x": 18, "y": 7 }, { "level": 4, "x": 19, "y": 7 }, { "level": 2, "x": 4, "y": 8 }, { "level": 3, "x": 5, "y": 8 }, { "level": 4, "x": 6, "y": 8 }, { "level": 4, "x": 7, "y": 8 }, { "level": 4, "x": 8, "y": 8 }, { "level": 3, "x": 9, "y": 8 }, { "level": 3, "x": 17, "y": 8 }, { "level": 4, "x": 18, "y": 8 }, { "level": 4, "x": 19, "y": 8 }, { "level": 3, "x": 20, "y": 8 }, { "level": 2, "x": 21, "y": 8 }, { "level": 3, "x": 7, "y": 9 }, { "level": 4, "x": 8, "y": 9 }, { "level": 4, "x": 9, "y": 9 }, { "level": 4, "x": 10, "y": 9 }, { "level": 4, "x": 11, "y": 9 }, { "level": 4, "x": 12, "y": 9 }, { "level": 4, "x": 13, "y": 9 }, { "level": 4, "x": 14, "y": 9 }, { "level": 4, "x": 15, "y": 9 }, { "level": 4, "x": 16, "y": 9 }, { "level": 4, "x": 17, "y": 9 }, { "level": 2, "x": 18, "y": 9 }, { "level": 2, "x": 0, "y": 10 }, { "level": 3, "x": 1, "y": 10 }, { "level": 4, "x": 2, "y": 10 }, { "level": 4, "x": 3, "y": 10 }, { "level": 3, "x": 4, "y": 10 }, { "level": 3, "x": 8, "y": 10 }, { "level": 4, "x": 9, "y": 10 }, { "level": 4, "x": 10, "y": 10 }, { "level": 4, "x": 11, "y": 10 }, { "level": 4, "x": 12, "y": 10 }, { "level": 4, "x": 13, "y": 10 }, { "level": 4, "x": 14, "y": 10 }, { "level": 4, "x": 15, "y": 10 }, { "level": 4, "x": 16, "y": 10 }, { "level": 4, "x": 17, "y": 10 }, { "level": 4, "x": 18, "y": 10 }, { "level": 2, "x": 2, "y": 11 }, { "level": 4, "x": 3, "y": 11 }, { "level": 4, "x": 4, "y": 11 }, { "level": 4, "x": 5, "y": 11 }, { "level": 4, "x": 6, "y": 11 }, { "level": 3, "x": 7, "y": 11 }, { "level": 4, "x": 8, "y": 11 }, { "level": 4, "x": 9, "y": 11 }, { "level": 4, "x": 10, "y": 11 }, { "level": 4, "x": 11, "y": 11 }, { "level": 4, "x": 12, "y": 11 }, { "level": 4, "x": 13, "y": 11 }, { "level": 4, "x": 14, "y": 11 }, { "level": 4, "x": 15, "y": 11 }, { "level": 4, "x": 16, "y": 11 }, { "level": 4, "x": 17, "y": 11 }, { "level": 4, "x": 18, "y": 11 }, { "level": 3, "x": 4, "y": 12 }, { "level": 4, "x": 5, "y": 12 }, { "level": 4, "x": 6, "y": 12 }, { "level": 4, "x": 7, "y": 12 }, { "level": 4, "x": 8, "y": 12 }, { "level": 4, "x": 9, "y": 12 }, { "level": 3, "x": 10, "y": 12 }, { "level": 4, "x": 11, "y": 12 }, { "level": 4, "x": 12, "y": 12 }, { "level": 3, "x": 13, "y": 12 }, { "level": 4, "x": 14, "y": 12 }, { "level": 4, "x": 15, "y": 12 }, { "level": 3, "x": 16, "y": 12 }, { "level": 4, "x": 17, "y": 12 }, { "level": 4, "x": 18, "y": 12 }, { "level": 2, "x": 5, "y": 13 }, { "level": 3, "x": 6, "y": 13 }, { "level": 3, "x": 7, "y": 13 }, { "level": 4, "x": 8, "y": 13 }, { "level": 4, "x": 9, "y": 13 }, { "level": 4, "x": 11, "y": 13 }, { "level": 4, "x": 12, "y": 13 }, { "level": 4, "x": 14, "y": 13 }, { "level": 4, "x": 15, "y": 13 }, { "level": 4, "x": 17, "y": 13 }, { "level": 4, "x": 18, "y": 13 }, { "level": 4, "x": 8, "y": 14 }, { "level": 4, "x": 9, "y": 14 }, { "level": 4, "x": 11, "y": 14 }, { "level": 4, "x": 12, "y": 14 }, { "level": 4, "x": 14, "y": 14 }, { "level": 4, "x": 15, "y": 14 }, { "level": 4, "x": 17, "y": 14 }, { "level": 4, "x": 18, "y": 14 }, { "level": 2, "x": 7, "y": 15 }, { "level": 4, "x": 8, "y": 15 }, { "level": 3, "x": 9, "y": 15 }, { "level": 4, "x": 11, "y": 15 }, { "level": 4, "x": 12, "y": 15 }, { "level": 4, "x": 14, "y": 15 }, { "level": 4, "x": 15, "y": 15 }, { "level": 3, "x": 17, "y": 15 }, { "level": 4, "x": 18, "y": 15 }, { "level": 2, "x": 19, "y": 15 }, { "level": 2, "x": 5, "y": 16 }, { "level": 4, "x": 6, "y": 16 }, { "level": 4, "x": 7, "y": 16 }, { "level": 3, "x": 8, "y": 16 }, { "level": 4, "x": 11, "y": 16 }, { "level": 3, "x": 12, "y": 16 }, { "level": 3, "x": 14, "y": 16 }, { "level": 4, "x": 15, "y": 16 }, { "level": 3, "x": 18, "y": 16 }, { "level": 4, "x": 19, "y": 16 }, { "level": 4, "x": 20, "y": 16 }, { "level": 2, "x": 21, "y": 16 }];
const octoCatData2 = octoCatData.reduce((a, b) => { a.push({ level: b.level, x: 22 - b.x, y: b.y }); return a; }, []);
//========================================
// fill functions
//----------------------------------------
const boxWidth = boxes.length;
const boxHeight = boxes[0].length;

function fillWhen(condition, colorLevel, xStart = 0, yStart = 0, xEnd = boxWidth, yEnd = boxHeight) {
   for (let i = xStart; i < xEnd; i++) {
      for (let j = yStart; j < yEnd; j++) {
         if (condition(i, j)) {
            fillCoord(i, j, colorLevel);
         }
      }
   }
}
const fillPartial = (xStart, yStart, xEnd, yEnd, colorLevel) => fillWhen(() => true, colorLevel, xStart, yStart, xEnd, yEnd);
const fillAll = (colorLevel) => fillPartial(0, 0, boxWidth, boxHeight, colorLevel);
//coloverlevel -1 : invert (default)
//coloverlevel -2 : add
//coloverlevel -3 : subtract
function fillCoord(x, y, colorLevel) {
   if (!boxes[x] || !boxes[x][y]) return;
   let target = boxes[x][y];
   if (colorLevel < 0) {
      switch (colorLevel) {
         case -2:
            if (target.dataset.level === 0) return;
            colorLevel = Math.min(target.dataset.level + 1, 4);
            break;
         case -3:
            colorLevel = Math.max(target.dataset.level - 1, 0);
            break;
         default:
            colorLevel = 4 - target.dataset.level;
            break;
      }
   }
   target.dataset.level = colorLevel;
}
//========================================
// text functions
//----------------------------------------
function drawText(data, xOffset, yOffset, color = 4, clearScreen = true, limit = boxWidth) {
   if (clearScreen) fillAll(0);
   //center text if x value is NaN
   if (isNaN(xOffset)) {
      xOffset = getRealOffset(data);
   }
   for (let coord of data) {
      if (coord.x + xOffset <= limit) {
         fillCoord(coord.x + xOffset, coord.y + yOffset, coord.level ? coord.level : color);
      }
   }
}
function getRealOffset (data) {
   let textPixelLength = data.reduce((a, b) => (b.x > a) ? b.x : a, -1);
   return Math.round((boxWidth - textPixelLength) / 2);
}
function adjustOffset(data, xOffset, yOffset) {
   for (var coord of data) {
      coord.x += xOffset;
      coord.y += yOffset;
   }
   return data;
}
function invertRange(data, fromX, toX, fromY = 0, toY = boxHeight) {
   for (let i = fromX; i < toX; i++) {
      for (let j = fromY; j < toY; j++) {
         let coord = data.findIndex(d => d.x === i && d.y === j);
         if (coord >= 0) {
            data.splice(coord, 1);
         }
         else {
            data.push({ x: i, y: j });
         }
      }
   }
   return data;
}
//========================================
// animate functions
//----------------------------------------
let wait = time => new Promise(res => setTimeout(res, time));
function animateText(textCoords, option, clearScreen = true) {
   let directionX = option.directionX;
   let directionY = option.directionY;
   let duration = option.duration;
   let callback = option.callback;
   let color = option.color ?? 4;
   let textPixelLength = textCoords.reduce((a, b) => (b.x > a) ? b.x : a, -1);
   let isXMain = false;
   if (directionX === 0 && directionY === 0) {
      drawText(textCoords, 0, 0, color);
      return;
   }
   let xStartPos, yStartPos, xEndPos, yEndPos;
   if (directionX < 0) {
      xStartPos = directionX === 0 ? 0 : boxes.length;
      xEndPos = directionX === 0 ? 0 : -textPixelLength;
   }
   else {
      xStartPos = directionX === 0 ? 0 : -textPixelLength;
      xEndPos = directionX === 0 ? 0 : boxes.length;
   }
   let yMultiplier = (directionY < 0) ? -1 : 1
   yStartPos = -yMultiplier * boxes[0].length;
   yEndPos = yMultiplier * boxes[0].length;
   if (directionY === 0) {
      isXMain = true;
      yStartPos = 0;
      yEndPos = 0;
   }
   let totalSteps = directionX === 0 ? Math.abs(yStartPos - yEndPos) : Math.abs(xStartPos - xEndPos);
   let steps = (totalSteps * (isXMain ? Math.abs(directionX) : Math.abs(directionY)));
   let xStep = directionX * Math.abs(xEndPos - xStartPos) / totalSteps;
   let yStep = directionY * Math.abs(yEndPos - yStartPos) / totalSteps;
   let currentX = xStartPos;
   let currentY = yStartPos;

   return animate(duration, steps, function (iter) {
      drawText(textCoords, Math.round(currentX), Math.round(currentY), color, clearScreen);
      currentX += xStep;
      currentY += yStep;
      if(callback) callback(iter);
   });
}
function animate(duration, steps, callback) {
   if (duration <= 0 || steps <= 0) return Promise.reject("Wrong time to animate!");
   let timeInterval = duration / steps;
   let timePassed = 0;
   let iter = 0;
   return new Promise(
      res => {
         let interval = setInterval(function () {
            if (timePassed >= duration) {
               clearInterval(interval);
               res();
            }
            callback(iter++);
            timePassed += timeInterval;
         }, timeInterval);
      }
   )
}
function moveToLeft() {
   for (let i = 0; i < boxWidth - 2; i++) {
      for (let j = 0; j < boxHeight; j++) {
         if (!boxes[i][j]) continue;
         let from = boxes[i + 1][j];
         let to = boxes[i][j];
         if (!to) continue;
         if (!from) to.dataset.level = 0;
         else to.dataset.level = from.dataset.level;
      }
   }
}
function moveToRight() {
   for (let i = 0; i < boxWidth - 2; i++) {
      for (let j = 0; j < boxHeight; j++) {
         if (!boxes[i][j]) continue;
         let to = boxes[i + 1][j];
         let from = boxes[i][j];
         if (!to) continue;
         if (!from) to.dataset.level = 0;
         else to.dataset.level = from.dataset.level;
      }
   }
}
// ----------------------- clear
fillAll(0);
// ----------------------- text
//
await wait(2000);
await animateText(font1.getPixels("SENPAI NOTICE MY CONTRIBUTION"), {directionX: -1, directionY: 0, duration: 10000, callback: function(iter) {
   if(iter%10==0) {
      if(iter===280) return;
       playSound(iter%40!=0?undefined:24, (iter%40==0?3:(iter%20==0?7:10))+12);
   }
}});
await wait(2000);
let currentX = 0;
await animate(2000, boxWidth + 3, function (iter) {
   playSound(3+iter, iter)
   fillPartial(currentX, 0, currentX + 1, boxHeight, 1);
   fillPartial(currentX - 1, 0, currentX, boxHeight, 2);
   fillPartial(currentX - 2, 0, currentX - 1, boxHeight, 3);
   fillPartial(currentX - 3, 0, currentX - 2, boxHeight, 4);
   currentX++;
});
let uwu = font1.getPixels("UwU");
drawText(uwu, NaN, 0, -1, false);
playSound(undefined, -15);
await animate(4000, 10, function (iter) {
   fillAll(-1);
   playSound(undefined, iter-12);
});
await animate(500, 4, function (iter) {
   drawText(uwu, NaN, 0, 4 - iter, false);
});
await wait(1500);
drawText(adjustOffset(font2.getPixels("ARE YOU"), -1, -2), NaN, 0);
playSound(0, 0);
await wait(2000);
drawText(adjustOffset(font2.getPixels("DEVELOPER"), -1, -2), 0, 0, 2);
playSound(2, 7);
await animate(1000, boxHeight + 1, function (iter) {
   fillPartial(0, iter, boxWidth, iter + 1, -2);
   if(iter%2==0) playSound(undefined, 5+iter*0.5);
});
await wait(2000);
await animate(1000, boxHeight + 1, function (iter) {
   fillPartial(0, boxHeight - iter, boxWidth, boxHeight + 1 - iter, -3);
   if(iter%2==0) playSound(undefined, 10-iter*0.5);
});
fillAll(0);
let contributionText = adjustOffset(font3.getPixels("IF YOUR CONTRIBUTION DOESN'T LOOK LIKE THIS:"), -1, -2);
invertRange(contributionText, 45, 120);
await animateText(contributionText, {directionX: -1 , directionY: 0, duration: 16000, callback: function(iter){
   if(iter%5===0){
      if(iter<90) {
         playSound((iter%40==0)?-2:(iter%20==0)?7:undefined, (iter%20==0)?5:7);
      }
      else if (iter<240) {
         playSound((iter%10==0)?-2:(iter%10==0)?7:undefined, (iter%10==0)?3:10);
      }
      else {
         playSound(3, 5)
      }
   }
}});
fillAll(0);
await animate(10000, 100, function (iter) {
   fillAll(0);
   fillWhen(
      (x, y) => Math.sin(x * (0.5 + iter % 0.3)) * (1 + iter % 2) + 2 < y
      , 2);
   fillWhen(
      (x, y) => Math.cos(x * (0.8 - iter % 0.3) + iter * 0.5) * (3 - iter % 2) + 2 < y
      , 4);
   if (iter > 50) {
      fillAll(-1);
      if(iter!==100) playSound((iter%10==0)?5:undefined, Math.sin(iter)*4+5);
   }
   else playSound((iter%10==0)?0:undefined, Math.cos(iter)*4);
});
await wait(500);
fillAll(-3);
await wait(500);
fillAll(-3);
await wait(500);
fillAll(-3);
await wait(500);
fillAll(-3);

let xd1 = font2.getPixels("XD");
let xd2 = font3.getPixels("XD");
await Promise.all([
   animateText(xd1, {directionX: -1, directionY: -1, duration: 2000, color: 1}, true),
   animateText(xd1, {directionX: 1, directionY: 1, duration: 2000, color: 2}, false),
   animateText(xd2, {directionX: -1, directionY: 1, duration: 2000, color: 3}, false),
   animateText(xd2, {directionX: 1, directionY: -1, duration: 2000, color: 4, callback: function(iter){
      switch(iter) {
         case 10:
            playSound(5,undefined);
            break;
         case 13:
            playSound(-2,undefined);
            break;
         case 25:
            playSound(undefined, 0);
            break;
      }
   }}, false)
]);
let gitForceSequence = [-1, 2, 4, 8, 12, 16, 20, 22, 26, 30]
let gitForceSequence2 = [9, 12, 14, 19, 23, 26, 30, 36, 45];
let blinkOpen = true;
let cursorX = gitForceSequence[0];
let drawCursor = () => fillPartial(cursorX + 1, 4, cursorX + 4, 6, blinkOpen ? 0 : 2);
await animate(4000, gitForceSequence.length * 2, function (iter) {
   cursorX = gitForceSequence[Math.floor(iter / 2)];
   blinkOpen = iter % 2 !== 0;
   if (cursorX === undefined) return;
   if (blinkOpen) {
      drawText(gitPushForce, 0, 0, 4, true, cursorX);
      playSound(24,undefined);
   }
   drawCursor();
})
await animate(2000, 20, function (iter) {
   moveToLeft();
   playSound(undefined,24);
})
await animate(4000, gitForceSequence2.length * 2, function (iter) {
   cursorX = gitForceSequence2[Math.floor(iter / 2)];
   blinkOpen = iter % 2 !== 0;
   if (cursorX === undefined) return;
   if (blinkOpen) {
      drawText(gitPushForce, -21, 0, 4, true, cursorX);
      playSound(24,undefined);
   }
   drawCursor();
})
await wait(2000);
await animate(2000, 2 * (boxWidth / 5), function (iter) {
   let direction = iter % 2 ? -1 : 1;
   let realIter = Math.floor(iter / 2);
   fillWhen((x, y) => y === Math.round(direction * (x - 5 * realIter) + 4), 4);
   playSound(undefined,iter%2==0?7:12);
});
await wait(500);
fillAll(-2);
await wait(500);
fillAll(-2);
await wait(1000);
await animate(10000, 50, function (iter) {
   let halfIter = Math.ceil(iter/2);
   drawText(octoCatData, 2, -halfIter + boxHeight);
   drawText(octoCatData2, 28, -17 + halfIter, 4, false);
   fillPartial(27, 0, boxWidth, boxHeight, -1);
   if(iter<48){
      if(iter%4==2) playSound(undefined, 7)
      else playSound((iter%4==3)?0:-7, undefined)
   }
});
let halfW = Math.round(boxWidth / 2);
await animate(2000, halfW, function (iter) {
   fillPartial(iter - 4, 2, iter - 3, 4, -1);
   fillPartial(boxWidth - iter + 5, 2, boxWidth - iter + 6, 4, -1);
   (iter%2==0)?playSound(iter, undefined):playSound(undefined, iter);
});
fillCircle(3, -1);
fillCircle(1, -1);
playSound([0, 24], [-12, 12]);
function fillCircle(radius, colorLevel) {
   if (radius <= 0) return;
   fillWhen((x, y) => (x - halfW) * (x - halfW) + (y - 3) * (y - 3) < (radius * radius) + 2, colorLevel)
}
await wait(2000);
await animate(2000, halfW - 2, function (iter) {
   fillPartial(iter, 0, iter + 1, boxHeight, -1);
   fillPartial(boxWidth - iter, 0, boxWidth - iter + 1, boxHeight, -1);
   (iter%2==0)?playSound(undefined, iter*2-12):playSound(iter*2-12, undefined);
})
fillPartial(halfW - 1, 0, halfW, boxHeight, -1);
playSound([0, 24], [-12, 24]);
await wait(2000);
let ripplePhase = 0;
await animate(8000, 100, function (iter) {
   let size = iter;
   fillCircle(size, 4);
   let isDark = true;
   size -= 4;
   while (size > 0) {
      fillCircle(size, isDark ? ripplePhase : 4);
      isDark = !isDark;
      size -= 4;
   };
   ripplePhase = Math.min(Math.floor(iter / 20), 4);
   if(iter%8==0) playSound(24, undefined);
   else playSound(undefined, 18)
});
const conflict = font3.getPixels("CONFLICT");
const realOffset = getRealOffset(conflict);
await wait(1000);
drawText(font3.getPixels("OOPS"), NaN, -2);
playSound([-12, 4],[-4, -8]);
await wait(1000);
drawText(font3.getPixels("MERGE"), NaN, -2);
playSound([-8, 12],[-4, 0]);
await wait(2000);
drawText(conflict, NaN, -2);
playSound([-8, 0, 4, 12],[-4, 0, 4]);
await animate(4000, 5, function () {
   fillAll(-1);
   playSound([-12, 0, 24], [10, 12])
});
await animate(2000, 20, function (iter) {
   drawText(conflict, realOffset-iter, -2, -2, false);
   drawText(conflict, realOffset+iter, -2, -3, false);
   playSound(undefined,24);
});
await animate(2000, 40, function (iter) {
   drawText(conflict, realOffset-iter, -2, -3, false);
   playSound(undefined,24);
});