added debug

This commit is contained in:
nvrl committed 2026-11-01 21:13:55 +01:00
1 parent cef3ffd240
commit 1074ece0c6
4 files changed
+46 -5

No files matched your search

+19 -1
View File
@@ -65,6 +65,8 @@ export interface DropState {
} }
export class LiquidBlob { export class LiquidBlob {
/// Why the last `create` gave up, for `?debug=drop`.
static failure = "";
readonly canvas = document.createElement("canvas"); readonly canvas = document.createElement("canvas");
/// Called if the GPU drops the context; the page shows plain art until `onRestored`. /// Called if the GPU drops the context; the page shows plain art until `onRestored`.
onLost: (() => void) | null = null; onLost: (() => void) | null = null;
@@ -113,7 +115,8 @@ export class LiquidBlob {
try { try {
return new LiquidBlob(touch); return new LiquidBlob(touch);
} catch (err) { } catch (err) {
console.warn("liquid visuals off:", (err as Error).message); LiquidBlob.failure = (err as Error).message;
console.warn("liquid visuals off:", LiquidBlob.failure);
return null; return null;
} }
} }
@@ -391,6 +394,21 @@ export class LiquidBlob {
); );
} }
/// What `?debug=drop` shows: the GPU, and how far the drop got.
describe(): string {
const gl = this.gl;
const info = gl.getExtension("WEBGL_debug_renderer_info");
const gpu = info ? gl.getParameter(info.UNMASKED_RENDERER_WEBGL) : gl.getParameter(gl.RENDERER);
const hp = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT);
return [
`gpu ${gpu}`,
`vertex highp ${hp ? hp.precision : "?"} bits`,
`ready ${this.ready} · lost ${gl.isContextLost()} · error ${gl.getError()}`,
`morph ${this.morph.toFixed(2)} · quality ${this.quality} · canvas ${this.canvas.width}x${this.canvas.height}`,
`in page ${this.canvas.isConnected} · css ${this.canvas.clientWidth}x${this.canvas.clientHeight}`,
].join("\n");
}
/// Stepped down to the lowest quality: the GPU had trouble keeping up. /// Stepped down to the lowest quality: the GPU had trouble keeping up.
get struggling(): boolean { get struggling(): boolean {
return this.quality === QUALITY.length - 1; return this.quality === QUALITY.length - 1;
+16 -1
View File
@@ -1,5 +1,6 @@
// `?debug=audio`: live bars of what features.ts hears, for tuning its constants by // `?debug=audio`: live bars of what features.ts hears, for tuning its constants by
// ear. Loaded only when asked for. // ear. `?debug=drop`: why the liquid drop does or does not show, readable on a
// phone without remote dev tools. Loaded only when asked for.
import type { Features } from "./features"; import type { Features } from "./features";
@@ -36,3 +37,17 @@ export function audioDebug(): (f: Features) => void {
}); });
}; };
} }
/// A box of text, rewritten at most a few times a second.
export function dropDebug(): (text: string) => void {
const el = document.createElement("pre");
el.className = "audio-debug drop-debug";
document.body.append(el);
let at = 0;
return (text) => {
const now = performance.now();
if (now - at < 250) return;
at = now;
el.textContent = text;
};
}
+2
View File
@@ -482,6 +482,8 @@ button.danger:not(.link) { background: var(--accent); border: none; color: #fff;
pointer-events: none; pointer-events: none;
} }
.drop-debug { margin: 0; padding: 0.4rem 0.5rem; max-width: calc(100vw - 1.4rem); font: 10px/1.35 ui-monospace, monospace; color: #fffc; white-space: pre-wrap; }
.card.paused .art { transform: scale(0.96); filter: brightness(0.7); } .card.paused .art { transform: scale(0.96); filter: brightness(0.7); }
.paused-badge { .paused-badge {
position: absolute; position: absolute;
+9 -3
View File
@@ -68,6 +68,7 @@ export class Visualizer {
private reduced = matchMedia("(prefers-reduced-motion: reduce)"); private reduced = matchMedia("(prefers-reduced-motion: reduce)");
private setting: VisualsSetting = localStorage.getItem(VISUALS_KEY) === "simple" ? "simple" : "liquid"; private setting: VisualsSetting = localStorage.getItem(VISUALS_KEY) === "simple" ? "simple" : "liquid";
private debug: ((f: Features) => void) | null = null; private debug: ((f: Features) => void) | null = null;
private dropDebug: ((text: string) => void) | null = null;
/// A press on the drop; `on` once it moved enough to turn it. /// A press on the drop; `on` once it moved enough to turn it.
private grab: { id: number; x: number; y: number; on: boolean } | null = null; private grab: { id: number; x: number; y: number; on: boolean } | null = null;
private spun = false; private spun = false;
@@ -103,9 +104,9 @@ export class Visualizer {
if (this.spun) e.stopPropagation(); if (this.spun) e.stopPropagation();
this.spun = false; this.spun = false;
}, true); }, true);
if (new URLSearchParams(location.search).get("debug") === "audio") { const debug = new URLSearchParams(location.search).get("debug");
import("./debug").then((m) => { this.debug = m.audioDebug(); }); if (debug === "audio") import("./debug").then((m) => { this.debug = m.audioDebug(); });
} if (debug === "drop") import("./debug").then((m) => { this.dropDebug = m.dropDebug(); this.start(); });
} }
get visuals(): VisualsSetting { get visuals(): VisualsSetting {
@@ -529,6 +530,11 @@ export class Visualizer {
? this.features.fromAnalyser(an, now / 1000, dt) ? this.features.fromAnalyser(an, now / 1000, dt)
: this.features.fromLoudness(card.loudnessAt(progress), now / 1000, dt); : this.features.fromLoudness(card.loudnessAt(progress), now / 1000, dt);
this.debug?.(f); this.debug?.(f);
this.dropDebug?.([
`setting ${this.setting} · reduced motion ${this.reduced.matches} · touch ${this.touch} · mixing ${mixed}`,
`blob ${this.blob === undefined ? "not asked" : this.blob === null ? `failed: ${LiquidBlob.failure}` : "made"} · card liquid ${!!card.el?.classList.contains("liquid")}`,
this.blob?.describe() ?? "",
].join("\n"));
let busy = this.shine(now, dt, f.kick) || playing; let busy = this.shine(now, dt, f.kick) || playing;
const liquid = !!this.blob && !!card.el?.classList.contains("liquid"); const liquid = !!this.blob && !!card.el?.classList.contains("liquid");
if (!this.reduced.matches) { if (!this.reduced.matches) {