feat: render audio-reactive glow on matrix grid

This commit is contained in:
clement
2026-06-28 18:09:09 +02:00
parent 0069b82875
commit 6c7cd45ecd
3 changed files with 66 additions and 1 deletions
+4 -1
View File
@@ -113,4 +113,7 @@ button.queued { animation: queued-blink 600ms ease-in-out infinite alternate; }
.mcell.m5 { background: #c60; border-color: #e80; }
.mcell.m6 { background: #82a; border-color: #a4d; }
.mcell.playing { box-shadow: 0 0 0 2px rgba(255,255,255,0.28),
inset 0 0 0 1px rgba(255,255,255,0.40); }
inset 0 0 0 1px rgba(255,255,255,0.40),
0 0 calc(var(--glow, 0) * 12px) calc(var(--glow, 0) * 4px)
rgba(255,255,255, calc(var(--glow, 0) * 0.7));
filter: brightness(calc(1 + var(--glow, 0) * 0.9)); }
+61
View File
@@ -161,6 +161,11 @@ ws.addEventListener("message", (ev) => {
}
return;
}
// /matrix/trig <vi> <amp> — per-note pulse: glow the active cell of voice vi
if (address === "/matrix/trig") {
triggerGlow(Math.round(Number(args[0])), Number(args[1]));
return;
}
});
// --- Matrix (16-voice x 32-bar song arranger) ---
@@ -254,6 +259,62 @@ function renderMatrix() {
}
}
// --- Matrix audio-reactive glow (trigger envelope; math in matrix_glow.js) ---
const voiceLevel = new Array(16).fill(0);
let glowRaf = null;
let glowLastTs = 0;
let glowAppliedBar = -1;
function clearGlowColumn(bar) {
if (bar < 0) return;
for (let vi = 0; vi < MATRIX_VOICES.length; vi++) {
const el = cellRefs[bar][vi];
if (el) el.style.removeProperty("--glow");
}
}
function glowFrame(ts) {
const decay = window.MatrixGlow ? window.MatrixGlow.nextGlow : (l) => 0;
const dt = glowLastTs ? (ts - glowLastTs) : (1000 / 60);
glowLastTs = ts;
if (matPlayhead !== glowAppliedBar) {
clearGlowColumn(glowAppliedBar);
glowAppliedBar = matPlayhead;
}
let anyActive = false;
for (let vi = 0; vi < 16; vi++) {
const lvl = decay(voiceLevel[vi], dt);
voiceLevel[vi] = lvl;
if (matPlayhead >= 0) {
const el = cellRefs[matPlayhead][vi];
if (el) {
if (lvl > 0) el.style.setProperty("--glow", lvl.toFixed(3));
else el.style.removeProperty("--glow");
}
}
if (lvl > 0) anyActive = true;
}
if (anyActive) {
glowRaf = requestAnimationFrame(glowFrame);
} else {
clearGlowColumn(glowAppliedBar);
glowRaf = null;
glowLastTs = 0;
glowAppliedBar = -1;
}
}
function triggerGlow(vi, amp) {
if (!Number.isInteger(vi) || vi < 0 || vi >= 16) return;
const a = Math.max(0, Math.min(1, amp));
if (a <= 0) return;
voiceLevel[vi] = Math.max(voiceLevel[vi], a);
if (glowRaf === null) {
glowLastTs = 0;
glowRaf = requestAnimationFrame(glowFrame);
}
}
// --- Pattern state (defaults mirror launchpad.scd exactly) ---
const STORAGE_KEY = "avlive.seq";
+1
View File
@@ -163,4 +163,5 @@
</div>
</section>
</div>
<script type="module">import * as G from "./matrix_glow.js"; window.MatrixGlow = G;</script>
<script src="control.js"></script></body></html>