Fix the flat drop on Linux and morph its play/pause in one shape

The kick wave in the vertex shader squared a negative number with pow(), which
GLSL leaves undefined. Mesa returns NaN there, so on Linux the drop's position
pinned to the clamp: it stayed a plain round sphere without wobble, and its
normals split it into a dark yin-yang. The squares are written out now, and the
normal comes from the slope of the displacement instead of two whole positions.

The play/pause icon is one shape whose corners move: each pause bar turns into
half of the play triangle, with a small swing in between, instead of two icons
faded over each other. It sits on a disc of frosted artwork with a lit rim, and a
ring round it shows the track's progress, or runs while the track loads, which
replaces the flat spinner. The surface calms down under the icon gradually
instead of jumping flat, a double-tap no longer flickers the icon, and a mouse
resting on the playing drop shows the pause bars faintly.

Also:
- Low-density desktop screens render the drop at twice the resolution.
- Quality steps follow the display's own frame rate and step back up again.
- A lost WebGL context brings the drop back once it is restored.
- A paused drop keeps breathing at a low frame rate on desktop.
- Without Web Audio (iOS) jumps in the waveform count as kicks, so the drop lands on drops there too.
- Artists without an image get their own colour from their name, so their drop no longer sinks into the page.
- z toggles zen mode: just the drop, bigger, and the music.
This commit is contained in:
nvrl committed 2026-11-01 19:04:18 +01:00
1 parent bcdca34244
commit 2dcca0bc94
9 files changed
+346 -110

No files matched your search

+13
View File
@@ -352,6 +352,7 @@ feedEl.addEventListener("click", (e) => {
if (lastTap.toggled) {
player.toggle();
card.clearFlash();
visual.cancelFlash();
}
card.burst(e.clientX, e.clientY);
setLike(card, true);
@@ -639,6 +640,7 @@ document.addEventListener("keydown", (e) => {
if ((e.target as Element).tagName === "INPUT" || e.ctrlKey || e.metaKey || e.altKey) return;
const k = e.key.length === 1 ? e.key.toLowerCase() : e.key;
if (k === "Escape") {
setZen(false);
closePanels();
hideHelp();
station.close();
@@ -666,9 +668,20 @@ document.addEventListener("keydown", (e) => {
else if (k === "m") { setMuted(!player.muted); toast(player.muted ? "muted" : "sound on"); }
else if (k === "+" || k === "=") { setVolume(player.volume + 0.1); toast(`volume ${Math.round(player.volume * 100)}%`); }
else if (k === "-") { setVolume(player.volume - 0.1); toast(`volume ${Math.round(player.volume * 100)}%`); }
else if (k === "z") setZen(!document.body.classList.contains("zen"));
else if (k === "?") { helpEl.hidden ? showHelp() : hideHelp(); }
});
/// Zen: nothing but the drop and the music.
function setZen(on: boolean): void {
if (document.body.classList.contains("zen") === on) return;
document.body.classList.toggle("zen", on);
if (on) {
closePanels();
toast("zen · z or esc to leave");
}
}
addEventListener("resize", () => current()?.drawWave(player.audio.duration ? player.audio.currentTime / player.audio.duration : 0, true));
addEventListener("pagehide", finishActive);