Spaces:
Running
Running
Download blipwren/app.js from CyberMax-tools/cybermax: direct link, hf CLI and curl.
- Browser
- Download file 9.7 kB
-
https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/blipwren/app.js
- Command line
-
hf download hf://spaces/CyberMax-tools/cybermax/blipwren/app.js
-
curl -L -o app.js https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/blipwren/app.js
9.7 kB
| /* Blipwren SFX player: Web Audio, no libraries. © 2026 CyberMax */ | |
| (function () { | |
| "use strict"; | |
| var CHECKOUT = "https://buy.polar.sh/polar_cl_AUuQ9JEVj98iOYnwpGKLQAqxklIRWpColFOBF1GXPFh"; // Polar checkout link (revenue/packs/polar/blipwren/polar.json) | |
| var BUNDLE = "https://buy.polar.sh/polar_cl_0VZrsUmgkJkVTDpll5E0TTHLI9eloGE5pmDyS4D9P8L"; // Indie Game Dev Bundle checkout link | |
| var $ = function (s) { return document.querySelector(s); }; | |
| var ctx = null, gain = null, meta = null, cat = "all", query = ""; | |
| var buffers = {}; // sprite id or "free:<id>" -> AudioBuffer | |
| var tiles = {}; // sound id -> {el, canvas, s} | |
| var current = null, playAllToken = 0; | |
| function audio() { | |
| if (!ctx) { | |
| var AC = window.AudioContext || window.webkitAudioContext; | |
| ctx = new AC(); | |
| gain = ctx.createGain(); | |
| gain.gain.value = $("#vol").value / 100; | |
| gain.connect(ctx.destination); | |
| } | |
| return ctx; | |
| } | |
| function toast(msg) { | |
| var t = $("#toast"); t.textContent = msg; t.classList.add("on"); | |
| clearTimeout(toast._t); toast._t = setTimeout(function () { t.classList.remove("on"); }, 3200); | |
| } | |
| function decode(url) { | |
| return fetch(url).then(function (r) { if (!r.ok) throw new Error(url + " " + r.status); return r.arrayBuffer(); }) | |
| .then(function (ab) { | |
| return new Promise(function (res, rej) { audio().decodeAudioData(ab, res, rej); }); | |
| }); | |
| } | |
| function load(s) { // full quality for free sounds, category sprite for the rest | |
| var key = s.free ? "free:" + s.id : s.sprite; | |
| if (buffers[key]) return Promise.resolve(buffers[key]); | |
| var url = s.free ? "audio/free/" + s.id + ".wav" : "audio/preview/" + s.sprite.toLowerCase() + ".wav"; | |
| buffers[key] = decode(url).then(function (b) { buffers[key] = b; return b; }); | |
| return buffers[key]; | |
| } | |
| function play(s) { | |
| audio(); | |
| if (ctx.state === "suspended") ctx.resume(); | |
| return Promise.resolve(load(s)).then(function (buf) { | |
| if (current) { try { current.src.stop(); } catch (e) {} current.tile.classList.remove("playing"); } | |
| var src = ctx.createBufferSource(); | |
| src.buffer = buf; | |
| if ($("#vary").checked) src.playbackRate.value = 1 + (Math.random() * 0.1 - 0.05); | |
| src.connect(gain); | |
| var tile = tiles[s.id].el; | |
| tile.classList.add("playing"); | |
| var dur = s.free ? buf.duration : s.len; | |
| if (s.free) src.start(0); else src.start(0, s.at, s.len); | |
| var me = current = { src: src, tile: tile }; | |
| src.onended = function () { tile.classList.remove("playing"); if (current === me) current = null; }; | |
| return dur / src.playbackRate.value; | |
| }).catch(function (e) { toast("Couldn't play that sound: " + e.message); return 0; }); | |
| } | |
| function drawWave(s) { | |
| var t = tiles[s.id]; if (!t) return; | |
| var buf = buffers[s.free ? "free:" + s.id : s.sprite]; | |
| if (!buf || typeof buf.then === "function") return; | |
| var c = t.canvas, dpr = window.devicePixelRatio || 1; | |
| var w = c.clientWidth || 180, h = c.clientHeight || 28; | |
| c.width = Math.round(w * dpr); c.height = Math.round(h * dpr); | |
| var g = c.getContext("2d"); g.scale(dpr, dpr); | |
| var data = buf.getChannelData(0), sr = buf.sampleRate; | |
| var a = s.free ? 0 : Math.floor(s.at * sr), n = s.free ? data.length : Math.floor(s.len * sr); | |
| var cols = Math.max(20, Math.floor(w / 2)), step = n / cols; | |
| g.fillStyle = s.free ? "#34d399" : "#FFB020"; | |
| for (var i = 0; i < cols; i++) { | |
| var peak = 0, from = a + Math.floor(i * step), to = a + Math.floor((i + 1) * step); | |
| for (var j = from; j < to; j += 4) { var v = Math.abs(data[j] || 0); if (v > peak) peak = v; } | |
| var bh = Math.max(1, peak * (h - 2)); | |
| g.fillRect(i * 2, (h - bh) / 2, 1.4, bh); | |
| } | |
| } | |
| function label(c) { return c === "all" ? "All" : meta.categories.filter(function (x) { return x.id === c; })[0].label; } | |
| function buildChips() { | |
| var box = $("#cats"), list = ["all"].concat(meta.categories.map(function (c) { return c.id; })); | |
| list.forEach(function (c) { | |
| var b = document.createElement("button"); | |
| b.className = "chip" + (c === cat ? " on" : ""); | |
| b.dataset.cat = c; | |
| var n = c === "all" ? meta.count : meta.sounds.filter(function (s) { return s.cat === c; }).length; | |
| b.innerHTML = label(c) + '<span class="n">' + n + "</span>"; | |
| b.addEventListener("click", function () { | |
| cat = c; | |
| Array.prototype.forEach.call(box.children, function (x) { x.classList.toggle("on", x.dataset.cat === c); }); | |
| filter(); | |
| }); | |
| box.appendChild(b); | |
| }); | |
| } | |
| function buildGrid() { | |
| var grid = $("#grid"); | |
| meta.sounds.forEach(function (s) { | |
| var el = document.createElement("button"); | |
| el.className = "snd"; el.type = "button"; | |
| el.setAttribute("aria-label", "Play " + s.name + (s.free ? " (free)" : "")); | |
| el.innerHTML = '<span class="nm"></span><span class="ds"></span><span class="dur"></span><canvas aria-hidden="true"></canvas>'; | |
| el.querySelector(".nm").textContent = s.name; | |
| el.querySelector(".ds").textContent = s.desc; | |
| el.querySelector(".dur").textContent = s.dur.toFixed(2) + "s"; | |
| if (s.free) { | |
| var foot = document.createElement("span"); foot.className = "foot"; | |
| var badge = document.createElement("span"); badge.className = "free-badge"; badge.textContent = "FREE"; | |
| foot.appendChild(badge); | |
| var dl = document.createElement("a"); dl.className = "dl"; dl.href = "audio/free/" + s.id + ".wav"; | |
| dl.download = "blipwren_" + s.id + ".wav"; dl.textContent = "WAV ↓"; | |
| dl.addEventListener("click", function (e) { e.stopPropagation(); }); | |
| foot.appendChild(dl); | |
| el.appendChild(foot); | |
| } | |
| el.addEventListener("click", function () { playAllToken++; play(s); }); | |
| tiles[s.id] = { el: el, canvas: el.querySelector("canvas"), s: s }; | |
| grid.appendChild(el); | |
| }); | |
| } | |
| function shown() { | |
| return meta.sounds.filter(function (s) { | |
| return (cat === "all" || s.cat === cat) && | |
| (!query || (s.name + " " + s.desc + " " + s.cat).toLowerCase().indexOf(query) >= 0); | |
| }); | |
| } | |
| function filter() { | |
| var vis = {}; shown().forEach(function (s) { vis[s.id] = 1; }); | |
| Object.keys(tiles).forEach(function (id) { tiles[id].el.hidden = !vis[id]; }); | |
| var n = Object.keys(vis).length; | |
| $("#status").textContent = n ? n + " sound" + (n === 1 ? "" : "s") + " · tap to play" : "No sounds match “" + query + "”."; | |
| Object.keys(vis).forEach(function (id) { drawWave(tiles[id].s); }); | |
| } | |
| function playAll() { | |
| var token = ++playAllToken, list = shown(), i = 0; | |
| function next() { | |
| if (token !== playAllToken || i >= list.length) return; | |
| var s = list[i++]; | |
| tiles[s.id].el.scrollIntoView({ block: "nearest", behavior: "smooth" }); | |
| play(s).then(function (d) { setTimeout(next, (d + 0.25) * 1000); }); | |
| } | |
| next(); | |
| } | |
| function preload() { // decode sprites (works while the AudioContext is still suspended) to draw waveforms | |
| var jobs = meta.categories.map(function (c) { return c.id; }); | |
| meta.sounds.forEach(function (s) { if (s.free) jobs.push(s); }); | |
| var i = 0; | |
| function step() { | |
| if (i >= jobs.length) { window.__blipwrenReady = true; return; } | |
| var j = jobs[i++], s = typeof j === "string" ? meta.sounds.filter(function (x) { return x.sprite === j && !x.free; })[0] : j; | |
| if (!s) return step(); | |
| Promise.resolve(load(s)).then(function () { | |
| meta.sounds.forEach(function (x) { if ((x.free ? "free:" + x.id : x.sprite) === (s.free ? "free:" + s.id : s.sprite)) drawWave(x); }); | |
| }).catch(function () {}).then(step); | |
| } | |
| step(); | |
| } | |
| function tabs() { | |
| Array.prototype.forEach.call(document.querySelectorAll("#tabs .chip"), function (b) { | |
| b.addEventListener("click", function () { | |
| Array.prototype.forEach.call(document.querySelectorAll("#tabs .chip"), function (x) { | |
| x.classList.toggle("on", x === b); x.setAttribute("aria-selected", x === b ? "true" : "false"); | |
| }); | |
| Array.prototype.forEach.call(document.querySelectorAll("pre[data-pane]"), function (p) { p.hidden = p.dataset.pane !== b.dataset.tab; }); | |
| }); | |
| }); | |
| } | |
| function links() { | |
| var buy = $("#buy"); | |
| if (/^https:\/\//.test(CHECKOUT)) buy.href = CHECKOUT; | |
| else { buy.textContent = "Coming soon"; buy.removeAttribute("href"); buy.classList.add("ghost"); } | |
| var bundle = $("#bundle"); | |
| if (/^https:\/\//.test(BUNDLE)) bundle.href = BUNDLE; else bundle.parentNode.hidden = true; | |
| } | |
| $("#vol").addEventListener("input", function () { if (gain) gain.gain.value = this.value / 100; }); | |
| $("#q").addEventListener("input", function () { query = this.value.trim().toLowerCase(); filter(); }); | |
| $("#playall").addEventListener("click", playAll); | |
| document.addEventListener("keydown", function (e) { if (e.key === "Escape") { playAllToken++; if (current) try { current.src.stop(); } catch (x) {} } }); | |
| tabs(); links(); | |
| if (/[?&]thanks=1/.test(location.search)) setTimeout(function () { toast("Thanks for your order! Your download link is in the Polar receipt email."); }, 800); | |
| fetch("sounds.json").then(function (r) { return r.json(); }).then(function (m) { | |
| meta = m; buildChips(); buildGrid(); filter(); preload(); | |
| var hash = decodeURIComponent((location.hash.match(/^#s=([\w]+)/) || [])[1] || ""); | |
| if (hash && tiles[hash]) tiles[hash].el.scrollIntoView({ block: "center" }); | |
| }).catch(function () { $("#status").textContent = "Couldn't load the sound list. Please reload the page."; }); | |
| window.addEventListener("resize", function () { if (meta) filter(); }); | |
| })(); | |