memoscan
← all memos

Memo 0x42a4205a…9adfcc on Ethereum

if (audioStarted) return; var Ctx = (typeof window !== "undefined") && (window.AudioContext || window.webkitAudioContext); if (!Ctx) return; AC = new Ctx(); master = AC.createGain(); master.gain.value = muted ? 0 : 0.5; master.connect(AC.destination); try { var b = AC.createBuffer(1, 1, AC.sampleRate); var s = AC.createBufferSource(); s.buffer = b; s.connect(AC.destination); s.start(0); } catch (e) {} buildSeq(); audioStarted = true; var begin = function () { stepIdx = 0; visT0 = nowSec(); nextStepTime = AC.currentTime + 0.06; }; begin(); if (AC.state === "suspended" && AC.resume) { try { AC.resume().then(begin).catch(function () {}); } catch (e) {} } schedTimer = setInterval(scheduler, TICK_MS); } function resumeAudio() { if (AC && AC.state === "suspended" && AC.resume) { try { AC.resume(); } catch (e) {} } } function scheduler() { if (!AC || !seq) return; if (AC.state !== "running") { resumeAudio(); return; } if (nextStepTime < AC.currentTime) nextStepTime = AC.currentTime + 0.02; while (nextStepTime < AC.currentTime + LOOKAHEAD) { playStep(stepIdx, nextStepTime); nextStepTime += seq.stepSeconds; stepIdx = (stepIdx + 1) % seq.steps; } } function blip(freq, time, dur, type, gain) { var o = AC.createOscillator(), g = AC.createGain(); o.type = type; o.frequency.setValueAtTime(freq, time); g.gain.setValueAtTime(0.0001, time); g.gain.linearRampToValueAtTime(gain, time + 0.006); g.gain.exponentialRampToValueAtTime(0.0001, time + dur); o.connect(g); g.connect(master); o.start(time); o.stop(time + dur + 0.02); } function kick(time) { var o = AC.createOscillator(), g = AC.createGain(); o.type = "sine"; o.frequency.setValueAtTime(150, time); o.frequency.exponentialRampToValueAtTime(45, time + 0.12); g.gain.setValueAtTime(0.6, time); g.gain.exponentialRampToValueAtTime(0.0001, time + 0.15); o.connect(g); g.connect(master); o.start(time); o.stop(time + 0.17); } function noiseBurst(time, dur, hp, gain) { var n = Math.floor(AC.sampleRate * dur); var buf = AC.createBuffer(1, n, AC.sampleRate), d = buf.getChannelData(0); for (var i = 0; i < n; i++) d[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / n, 2); var src = AC.createBufferSource(); src.buffer = buf; var f = AC.createBiquadFilter(); f.type = "highpass"; f.frequency.value = hp; var g = AC.createGain(); g.gain.value = gain; src.connect(f); f.connect(g); g.connect(master); src.start(time); } function playStep(i, time) { var w = (seq.waves && seq.waves[i]) || seq.leadWave || "square"; var L = seq.lead[i]; if (L != null) blip(midiToFreq(L), time, seq.stepSeconds * 0.85, w, (seq.vel && seq.vel[i]) || 0.16); var B = seq.bass[i]; if (B != null) blip(midiToFreq(B), time, seq.stepSeconds * 0.85, seq.bassWave || "square", 0.24); var p = seq.perc[i]; if (p & 2) kick(time); if (p & 1) noiseBurst(time, 0.025, 7000, 0.14); if (p & 8) noiseBurst(time, 0.11, 5000, 0.13); if (p & 4) noiseBurst(time, 0.12, 1400, 0.30); } function toggleMute() { muted = !muted; if (master) master.gain.value = muted ? 0 : 0.5; } function playChime(freq) { if (!AC || AC.state !== "running") return; var t = AC.currentTime; blip(freq || 880, t, 0.10, "square", 0.18); blip((freq || 880) * 1.5, t + 0.06, 0.12, "square", 0.16); } function playBoom(count, colorHex) { if (!AC || AC.state !== "running") return; var t = AC.currentTime; noiseBurst(t, 0.18, 700, 0.30); var m = metaFor(colorHex), f = midiToFreq(m.note); var o = AC.createOscillator(), g = AC.createGain(); o.type = m.wave; o.frequency.setValueAtTime(f, t); o.frequency.exponentialRampToValueAtTime(Math.max(40, f * 0.45), t + 0.16); g.gain.setValueAtTime(0.26, t); g.gain.exponentialRampToValueAtTime(0.0001, t + 0.2); o.connect(g); g.connect(master); o.start(t); o.stop(t + 0.22); if (count >= 3) for (var k = 0; k < 3; k++) blip(f * Math.pow(2, (k / 12) * 2), t + 0.05 + k * 0.06, 0.09, m.wave, 0.16); } /* ============================ GAME LOGIC ============================ */ function enterDemo(now) { roundState = "demo"; score = 0; multUntil = 0; flashUntil = 0; lastAuto = now; } function startRound(now) { score = 0; multUntil = 0; flashUntil = 0; roundState = "run"; roundStart = now; lastInteraction = now; } function nextPiece() { curHash = E.makeHash(); grid = E.buildGrid(curHash); if (audioStarted) buildSeq(); } function findMatchCell() { var cols = grid.cols, rows = grid.rows; function col(r, c) { return grid.cells[r * cols + c].creature.colors.stroke; } for (var r = 0; r < rows; r++) { var run = 1; for (var c = 1; c < cols; c++) { if (col(r, c) === col(r, c - 1)) { if (++run >= 3) return r * cols + c; } else run = 1; } } for (var c2 = 0; c2 < cols; c2++) { var run2 = 1; for (var r2 = 1; r2 < rows; r2++) { if (col(r2, c2) === col(r2 - 1, c2)) { if (++run2 >= 3) return r2 * cols + c2; } else run2 = 1; } } return -1; } function autoPlayStep(now) { if (!grid) return; var idx = (Math.random() < 0.6) ? findMatchCell() : -1; if (idx < 0) idx = (Math.random() * grid.cells.length) | 0; if (grid.cells[idx].fx) return; popCell(idx, (idx / grid.cols) | 0, idx % grid.cols, now); } function explodeSet(indices, now) { var out = []; for (var i = 0; i < indices.length; i++) if (E.explode(grid, indices[i], now)) out.push(indices[i]); return out; } function popCell(idx, row, col, now) { var cr = grid.cells[idx].creature, bonus = cr.bonus, tapColor = cr.colors.stroke; var mult = (now < multUntil) ? 2 : 1; if (bonus) { var NAMES = { bomb: "BOMB", gold: "GOLD", time: "TIME", x2: "X2", rainbow: "COLOR" }; flashText = NAMES[bonus] || bonus.toUpperCase(); flashUntil = now + 1.3; flashCol = (E.BONUS && E.BONUS[bonus] && E.BONUS[bonus].col) || "#ffffff"; } var done = [], extra = 0, i; if (bonus === "bomb") { var set = []; for (var c = 0; c < grid.cols; c++) set.push(row * grid.cols + c); for (var r = 0; r < grid.rows; r++) set.push(r * grid.cols + col); done = explodeSet(set, now); } else if (bonus === "rainbow") { var color = cr.colors.stroke, set2 = []; for (i = 0; i < grid.cells.length; i++) if (grid.cells[i].creature.colors.stroke === color) set2.push(i); done = explodeSet(set2, now); } else if (bonus === "gold") { done = E.explodeMatch(grid, idx, now); extra = 100; } else if (bonus === "time") { roundStart += 4; done = E.explodeMatch(grid, idx, now); playChime(880); } else if (bonus === "x2") { multUntil = now + 6; done = E.explodeMatch(grid, idx, now); playChime(1046); } else { done = E.explodeMatch(grid, idx, now); if (!done.length) return; } score += mult * (scoreForCells(done) + extra); playBoom(done.length, tapColor); } /* ============================ INPUT ============================ */ function onPointer(ev) { startAudio(); resumeAudio(); if (!grid) return; var now = nowSec(); var rect = canvas.getBoundingClientRect(); var cx = (typeof ev.clientX === "number") ? ev.clientX : (ev.touches && ev.touches[0] ? ev.touches[0].clientX : 0); var cy = (typeof ev.clientY === "number") ? ev.clientY : (ev.touches && ev.touches[0] ? ev.touches[0].clientY : 0); var mx = (cx - rect.left) * (canvas.width / rect.width); var my = (cy - rect.top) * (canvas.height / rect.height); var bx = (mx - dispOX) / dispScale, by = (my - dispOY) / dispScale; lastInteraction = now; if (roundState !== "run") startRound(now); if (bx < 0 || by < 0 || bx >= CW || by >= CH) return; var cwN = grid.caw * E.BLK, chN = grid.cah * E.BLK; var col = Math.floor(bx / cwN), row = Math.floor(by / chN); if (col < 0 || col >= grid.cols || row < 0 || row >= grid.rows) return; popCell(row * grid.cols + col, row, col, now); } function onKey(e) { startAudio(); resumeAudio(); lastInteraction = nowSec(); var k = (e.key || "").toLowerCase(); if (k === "m") toggleMute(); else if (k === "n") { nextPiece(); enterDemo(nowSec()); } else if (k === "r") startRound(nowSec()); else if (k === "s") downloadImage(); else if (k === "p") shareScore(); } if (typeof addEventListener !== "undefined") { addEventListener("pointerdown", onPointer); addEventListener("keydown", onKey); } if (typeof document !== "undefined" && document.addEventListener) document.addEventListener("visibilitychange", function () { if (!document.hidden) resumeAudio(); }); /* ============================ SHARE / SAVE ============================ */ function makeShareImage() { var scale = 3, pad = 10, titleH = 44, scoreH = 60; var gW = CW * scale, W = gW + pad * 2, H = titleH + CH * scale + scoreH + pad * 2; var cv = document.createElement("canvas"); cv.width = W; cv.height = H; var c2 = cv.getContext("2d"); c2.fillStyle = "#05060f"; c2.fillRect(0, 0, W, H); drawText(c2, "PXL BITS", W / 2, pad + 14, 3, "#00e5ff", "center"); c2.imageSmoothingEnabled = false; c2.drawImage(off, pad, titleH, gW, CH * scale); drawText(c2, score + " BITS", W / 2, titleH + CH * scale + pad + 22, 6, "#ffe600", "center"); return cv; } function downloadURL(url, name) { try { var a = document.createElement("a"); a.href = url; a.download = name; (document.body || document.documentElement).appendChild(a); a.click(); try { a.remove(); } catch (e) {} } catch (e) {} } function downloadImage() { var cv = makeShareImage(), name = "pxl-bits-" + score + "bits.png"; if (cv.toBlob) cv.toBlob(function (b) { if (b) downloadURL(URL.createObjectURL(b), name); else downloadURL(cv.toDataURL("image/png"), name); }, "image/png"); else downloadURL(cv.toDataURL("image/png"), name); } function shareScore() { var cv = makeShareImage(), name = "pxl-bits-" + score + "bits.png"; var text = "PXL BITS — I scored " + score + " BITS"; var withBlob = function (blob) { if (typeof window !== "undefined" && typeof window.ARTSUNAMI_SHARE === "function") { try { window.ARTSUNAMI_SHARE(blob, text); return; } catch (e) {} } try { var file = new File([blob], name, { type: "image/png" }); if (navigator.share && navigator.canShare && navigator.canShare({ files: [file] })) { navigator.share({ files: [file], title: "PXL BITS", text }).catch(function () { downloadURL(URL.createObjectURL(blob), name); }); return; } } catch (e) {} downloadURL(URL.createObjectURL(blob), name); }; if (cv.toBlob) cv.toBlob(function (b) { if (b) withBlob(b); else downloadURL(cv.toDataURL("image/png"), name); }, "image/png"); else downloadURL(cv.toDataURL("image/png"), name); } /* ============================ RENDER LOOP ============================ */ function render() { var rawT = nowSec(); var t = audioStarted ? (rawT - visT0) : rawT; E.paint(fb, grid, t, rawT); var src = fb.d, px = imgData.data; for (var i = 0, j = 0; i < src.length; i++, j += 4) { var col = src[i]; if (col) { var r = rgbOf(col); px[j] = r[0]; px[j + 1] = r[1]; px[j + 2] = r[2]; } else { px[j] = BG[0]; px[j + 1] = BG[1]; px[j + 2] = BG[2]; } px[j + 3] = 255; } offctx.putImageData(imgData, 0, 0); var W = canvas.width, H = canvas.height, s = W / CW, headerPx = HDR * s; ctx.fillStyle = "#05060f"; ctx.fillRect(0, 0, W, H); ctx.imageSmoothingEnabled = false; ctx.drawImage(off, 0, headerPx, W, CH * s); dispScale = s; dispOX = 0; dispOY = headerPx; var now = rawT; if (roundState === "demo") { if (now - lastAuto > AUTO_EVERY) { autoPlayStep(now); lastAuto = now; } } else if (roundState === "run") { if (now - lastInteraction > DEMO_RETURN) enterDemo(now); else if (now - roundStart >= ROUND) { roundStart = now; score = 0; multUntil = 0; flashUntil = 0; } } var remain = roundState === "run" ? Math.max(0, ROUND - (now - roundStart)) : ROUND; var timeLabel = "" + Math.ceil(remain), scoreLabel = "" + score; var ps = Math.max(2, Math.floor(headerPx * 0.52 / 5)), cy = headerPx / 2, pad = Math.max(1, Math.round(2 * s)); if (roundState === "run") { drawText(ctx, scoreLabel, pad, cy, ps, "#ffe600", "left", "#0b1280", 2); drawText(ctx, timeLabel, W - pad, cy, ps, remain <= 10 ? "#ff2740" : "#ffffff", "right", "#0b1280", 2); } if (now < flashUntil) drawText(ctx, flashText, W / 2, cy, ps, flashCol, "center", "#0b1280", 2); else if (roundState === "run" && now < multUntil) drawText(ctx, "X2", W / 2, cy, ps, "#ff17c6", "center", "#0b1280", 2); else if (roundState === "demo") drawText(ctx, "PXL BITS", W / 2, cy, ps, "#ffe600", "center", "#0b1280", 2); (typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : function (f) { setTimeout(f, 16); })(render); } render();