taste canvases read the theme's colours instead of hard-coded dark ones
shots / shots (push) Successful in 2m22s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6
This commit is contained in:
teahubxyzandClaude Fable 5.1 committed 2026-10-20 23:53:40 +00:00
1 parent 2beac64165
commit 83bf464b49
1 file changed
+11 -10
+11 -10
View File
@@ -165,12 +165,13 @@ const guessText = (spec) => spec && spec.guess != null ? ` · ${Math.round(spec.
})();
/* ---------- TASTE: the brain and the map ---------- */
const cssv = (n) => getComputedStyle(document.documentElement).getPropertyValue(n).trim();
function drawBrain(canvas, net, spec, pulse = 0) {
const ctx = canvas.getContext("2d"), W = canvas.width, H = canvas.height;
ctx.clearRect(0, 0, W, H); ctx.fillStyle = "#121216"; ctx.fillRect(0, 0, W, H);
ctx.clearRect(0, 0, W, H); ctx.fillStyle = cssv("--card"); ctx.fillRect(0, 0, W, H);
const cols = [W * 0.16, W * 0.5, W * 0.86], ins = ["tempo", "energy", "key·x", "key·y", "minor", "words"];
const yI = (i) => H * (0.12 + 0.76 * i / 5), yH = (i) => H * (0.07 + 0.86 * i / (NH - 1)), yO = H / 2;
if (!net) { ctx.fillStyle = "#8a8a96"; ctx.font = `${W * 0.035}px -apple-system, system-ui, sans-serif`; ctx.textAlign = "center"; ctx.fillText("react to three tracks and the brain wakes up", W / 2, H / 2); return; }
if (!net) { ctx.fillStyle = cssv("--dim"); ctx.font = `${W * 0.035}px -apple-system, system-ui, sans-serif`; ctx.textAlign = "center"; ctx.fillText("react to three tracks and the brain wakes up", W / 2, H / 2); return; }
const x = spec ? feats(spec) : new Array(NIN).fill(0), { h, y } = net.forward(x);
// the words block is shown as one input: its activation is how much of the song's wording the net recognises
let wordsAct = 0; for (let j = NUM; j < NIN; j++) wordsAct += Math.abs(x[j]); wordsAct = Math.min(1, wordsAct / 3);
@@ -185,20 +186,20 @@ function drawBrain(canvas, net, spec, pulse = 0) {
const glow = Math.abs(a) * (1 + pulse);
ctx.beginPath(); ctx.arc(cx, cy, r + 10 * glow, 0, Math.PI * 2); ctx.fillStyle = `hsl(${a >= 0 ? 44 : 220} 80% 65% / ${0.18 * glow})`; ctx.fill();
ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.fillStyle = `hsl(${a >= 0 ? 44 : 220} ${40 + 50 * Math.abs(a)}% ${35 + 35 * Math.abs(a)}%)`; ctx.fill();
if (label) { ctx.fillStyle = "#8a8a96"; ctx.font = `600 ${W * 0.026}px ui-monospace, Menlo, monospace`; ctx.textAlign = "right"; ctx.fillText(label, cx - r - 8, cy + 4); }
if (label) { ctx.fillStyle = cssv("--dim"); ctx.font = `600 ${W * 0.026}px ui-monospace, Menlo, monospace`; ctx.textAlign = "right"; ctx.fillText(label, cx - r - 8, cy + 4); }
};
shown.forEach((v, j) => node(cols[0], yI(j), W * 0.018, v, ins[j]));
h.forEach((v, k) => node(cols[1], yH(k), W * 0.017, v));
node(cols[2], yO, W * 0.034, y * 2 - 1);
ctx.fillStyle = "#ededf0"; ctx.font = `600 ${W * 0.03}px ui-monospace, Menlo, monospace`; ctx.textAlign = "left"; ctx.fillText(`${Math.round(y * 100)}%`, cols[2] + W * 0.05, yO + 5);
ctx.fillStyle = "#8a8a96"; ctx.font = `${W * 0.024}px ui-monospace, Menlo, monospace`; ctx.fillText(spec ? "would like this" : "", cols[2] + W * 0.05, yO + 5 + W * 0.03);
ctx.fillStyle = cssv("--ink"); ctx.font = `600 ${W * 0.03}px ui-monospace, Menlo, monospace`; ctx.textAlign = "left"; ctx.fillText(`${Math.round(y * 100)}%`, cols[2] + W * 0.05, yO + 5);
ctx.fillStyle = cssv("--dim"); ctx.font = `${W * 0.024}px ui-monospace, Menlo, monospace`; ctx.fillText(spec ? "would like this" : "", cols[2] + W * 0.05, yO + 5 + W * 0.03);
}
function drawTaste(rows, title, net) {
const c = $("#tasteMap"), ctx = c.getContext("2d"), W = c.width, H = c.height, pad = 70;
const X = (bpm) => pad + (Math.min(180, Math.max(60, bpm)) - 60) / 120 * (W - 2 * pad);
const Y = (en) => H - pad - Math.min(1, Math.max(0, en)) * (H - 2 * pad);
const pts = rows.map((t) => ({ x: X(+t.spec.bpm || 100), y: Y(+t.spec.energy || 0.5), w: reactionWeight(t.spec, t), hue: keyHue(t.spec.key), minor: /m(in|inor)?\b/i.test(t.spec.key || "") && !/maj/i.test(t.spec.key || ""), t }));
ctx.fillStyle = "#121216"; ctx.fillRect(0, 0, W, H);
ctx.fillStyle = cssv("--card"); ctx.fillRect(0, 0, W, H);
// the field: the net's prediction, in the station's usual key; lit gold where it says yes, blue where no
const keys = {}; for (const t of rows) keys[t.spec.key] = (keys[t.spec.key] || 0) + 1;
const key = Object.entries(keys).sort((a, b) => b[1] - a[1])[0]?.[0] || "A minor";
@@ -212,8 +213,8 @@ function drawTaste(rows, title, net) {
ctx.fillStyle = v > 0 ? `hsl(44 60% 60% / ${0.5 * Math.abs(v)})` : `hsl(220 30% 45% / ${0.45 * Math.abs(v)})`;
ctx.fillRect(gx, gy, cell, cell);
}
ctx.strokeStyle = "#232329"; ctx.lineWidth = 2; ctx.strokeRect(pad, pad, W - 2 * pad, H - 2 * pad);
ctx.fillStyle = "#8a8a96"; ctx.font = `600 ${W * 0.018}px ui-monospace, Menlo, monospace`; ctx.textAlign = "center";
ctx.strokeStyle = cssv("--line"); ctx.lineWidth = 2; ctx.strokeRect(pad, pad, W - 2 * pad, H - 2 * pad);
ctx.fillStyle = cssv("--dim"); ctx.font = `600 ${W * 0.018}px ui-monospace, Menlo, monospace`; ctx.textAlign = "center";
for (const b of [60, 90, 120, 150, 180]) ctx.fillText(b + "", X(b), H - pad + 34);
ctx.textAlign = "right"; for (const e of [0, 0.5, 1]) ctx.fillText(e === 0 ? "calm" : e === 1 ? "wild" : "·", pad - 14, Y(e) + 6);
// the points, with the listener's emoji over them
@@ -227,12 +228,12 @@ function drawTaste(rows, title, net) {
const em = reactionEmoji(p.t.spec.reaction);
const tg = tagSignals(p.t.spec), tagMark = tg.length ? ` ${tg.filter((s) => s.like).length}▲${tg.filter((s) => !s.like).length}▼` : "";
const tag = [...em].slice(0, 4).join("") + ((p.t.plays || 0) > 1 ? `×${p.t.plays}` : "") + tagMark;
if (tag) { ctx.font = `${W * 0.024}px -apple-system, system-ui, sans-serif`; ctx.fillStyle = "#ededf0"; ctx.fillText(tag, p.x, p.y - r - 8); }
if (tag) { ctx.font = `${W * 0.024}px -apple-system, system-ui, sans-serif`; ctx.fillStyle = cssv("--ink"); ctx.fillText(tag, p.x, p.y - r - 8); }
}
// the track in the oven, as a hollow mark
const nx = state.nextJob && state.nextJob.spec;
if (nx && rows.some((t) => t.spec.stationId === nx.stationId || title === "ALL")) {
ctx.beginPath(); ctx.arc(X(+nx.bpm || 100), Y(+nx.energy || 0.5), W * 0.014, 0, Math.PI * 2); ctx.setLineDash([4, 4]); ctx.lineWidth = 2; ctx.strokeStyle = "#ededf0"; ctx.stroke(); ctx.setLineDash([]);
ctx.beginPath(); ctx.arc(X(+nx.bpm || 100), Y(+nx.energy || 0.5), W * 0.014, 0, Math.PI * 2); ctx.setLineDash([4, 4]); ctx.lineWidth = 2; ctx.strokeStyle = cssv("--ink"); ctx.stroke(); ctx.setLineDash([]);
}
const warm = pts.filter((p) => p.w >= 0.25).length, cold = pts.filter((p) => p.w <= -0.25).length;
$("#tasteNote").textContent = !pts.length ? "Nothing heard here yet." :