mirror of
https://github.com/narrrl/pl8list-rewind.git
synced 2026-11-03 16:41:26 +01:00
added grain + spiky blob
This commit is contained in:
1 parent
1074ece0c6
commit
a04627c1ec
3 files changed
+53
-8
No files matched your search
+19
-5
@@ -108,7 +108,7 @@ uniform float uDist; // camera distance
|
|||||||
uniform float uTime; // flow time, runs with the tempo
|
uniform float uTime; // flow time, runs with the tempo
|
||||||
uniform float uMorph; // 0 flat disc (the static artwork), 1 liquid
|
uniform float uMorph; // 0 flat disc (the static artwork), 1 liquid
|
||||||
// Music, see features.ts.
|
// Music, see features.ts.
|
||||||
uniform float uLevel, uWobble, uKickAge, uSnare, uDrop, uBass, uGrowl;
|
uniform float uLevel, uWobble, uKick, uKickAge, uSnare, uDrop, uBass, uGrowl;
|
||||||
// Character of the track, see style.ts.
|
// Character of the track, see style.ts.
|
||||||
uniform float uGain, uSpike, uStep, uTwist, uRipple, uShock;
|
uniform float uGain, uSpike, uStep, uTwist, uRipple, uShock;
|
||||||
// The jump on prominent hits, from the springs in blob.ts.
|
// The jump on prominent hits, from the springs in blob.ts.
|
||||||
@@ -144,6 +144,10 @@ float disp(vec3 p) {
|
|||||||
float ripples = snoise(q * 3.2 + vec3(t * 1.3, -t, 0.0)) * (0.012 + 0.035 * uWobble + 0.045 * uGrowl * uBass) * uRipple;
|
float ripples = snoise(q * 3.2 + vec3(t * 1.3, -t, 0.0)) * (0.012 + 0.035 * uWobble + 0.045 * uGrowl * uBass) * uRipple;
|
||||||
float n = snoise(q * 2.4 - vec3(0.0, t * 0.6, t * 0.9));
|
float n = snoise(q * 2.4 - vec3(0.0, t * 0.6, t * 0.9));
|
||||||
float spikes = pow(max(n, 0.0), 3.0) * (0.05 + 0.25 * uSnare + 0.15 * uGrowl * uBass + 0.1 * uDrop) * uSpike;
|
float spikes = pow(max(n, 0.0), 3.0) * (0.05 + 0.25 * uSnare + 0.15 * uGrowl * uBass + 0.1 * uDrop) * uSpike;
|
||||||
|
// Thorns: the crests of a finer noise, sharpened into points that shoot out on
|
||||||
|
// snares, kicks and drops. 1 - |n| is never negative, so pow() is safe.
|
||||||
|
float crest = 1.0 - abs(snoise(q * 4.5 + vec3(t * 0.8, t * 0.5, -t * 0.4)));
|
||||||
|
float thorns = pow(crest, 6.0) * (0.02 + 0.2 * uSnare + 0.08 * uKick + 0.12 * uDrop + 0.1 * uGrowl * uBass) * uSpike;
|
||||||
// After each kick a wave runs from the front of the drop round to the back.
|
// After each kick a wave runs from the front of the drop round to the back.
|
||||||
// Squares are written out: pow() of a negative base is undefined in GLSL, and
|
// Squares are written out: pow() of a negative base is undefined in GLSL, and
|
||||||
// Mesa really returns NaN for it, which flattened the whole drop on Linux.
|
// Mesa really returns NaN for it, which flattened the whole drop on Linux.
|
||||||
@@ -153,7 +157,7 @@ float disp(vec3 p) {
|
|||||||
float near = distance(p, uPointer.xyz) * 2.2;
|
float near = distance(p, uPointer.xyz) * 2.2;
|
||||||
float dent = -0.18 * uPointer.w * exp(-near * near);
|
float dent = -0.18 * uPointer.w * exp(-near * near);
|
||||||
// Calmer while the icon shows, so it stays readable.
|
// Calmer while the icon shows, so it stays readable.
|
||||||
return clamp((lobes + sway + ripples + spikes + wave) * uGain + dent, -0.3, 0.4) * (1.0 - 0.6 * uCalm);
|
return clamp((lobes + sway + ripples + spikes + thorns + wave) * uGain + dent, -0.3, 0.5) * (1.0 - 0.6 * uCalm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -188,7 +192,7 @@ in vec3 vObj;
|
|||||||
|
|
||||||
uniform sampler2D uArt;
|
uniform sampler2D uArt;
|
||||||
uniform vec3 uAccent;
|
uniform vec3 uAccent;
|
||||||
uniform float uTime, uMorph, uKick, uDrop, uBass, uGrowl;
|
uniform float uTime, uMorph, uKick, uSnare, uDrop, uBass, uGrowl;
|
||||||
uniform float uHue; // rim hue offset, radians
|
uniform float uHue; // rim hue offset, radians
|
||||||
uniform float uIri; // how much the rim hue drifts
|
uniform float uIri; // how much the rim hue drifts
|
||||||
uniform float uMetal; // sharper, brighter highlights
|
uniform float uMetal; // sharper, brighter highlights
|
||||||
@@ -216,7 +220,7 @@ void main() {
|
|||||||
// Looking through the surface bends the artwork like water does.
|
// Looking through the surface bends the artwork like water does.
|
||||||
vec2 bend = n.xy * (0.22 + 0.1 * uDrop) * uMorph;
|
vec2 bend = n.xy * (0.22 + 0.1 * uDrop) * uMorph;
|
||||||
vec2 uv = 0.5 + vec2(vObj.x, -vObj.y) * 0.48 - vec2(bend.x, -bend.y) * 0.5;
|
vec2 uv = 0.5 + vec2(vObj.x, -vObj.y) * 0.48 - vec2(bend.x, -bend.y) * 0.5;
|
||||||
vec2 split = n.xy * (0.004 + 0.02 * uKick + 0.008 * uGrowl * uBass) * uMorph;
|
vec2 split = n.xy * (0.008 + 0.025 * uKick + 0.015 * uSnare + 0.01 * uGrowl * uBass) * uMorph;
|
||||||
// Turned sideways the projection smears; blur it into flowing colour instead.
|
// Turned sideways the projection smears; blur it into flowing colour instead.
|
||||||
float blur = 4.0 * pow(1.0 - abs(vObj.z), 3.0) * uMorph;
|
float blur = 4.0 * pow(1.0 - abs(vObj.z), 3.0) * uMorph;
|
||||||
vec3 art = vec3(texture(uArt, uv + split, blur).r, texture(uArt, uv, blur).g, texture(uArt, uv - split, blur).b);
|
vec3 art = vec3(texture(uArt, uv + split, blur).r, texture(uArt, uv, blur).g, texture(uArt, uv - split, blur).b);
|
||||||
@@ -228,9 +232,19 @@ void main() {
|
|||||||
// card's colours.
|
// card's colours.
|
||||||
vec3 rim = hueRotate(uAccent, uHue + uIri * (fres + 0.5 * sin(uTime * 0.3)));
|
vec3 rim = hueRotate(uAccent, uHue + uIri * (fres + 0.5 * sin(uTime * 0.3)));
|
||||||
rim = mix(rim, vec3(1.0, 0.35, 0.1), uHeat * 0.6);
|
rim = mix(rim, vec3(1.0, 0.35, 0.1), uHeat * 0.6);
|
||||||
|
// Oil-slick fringes: each channel cycles at its own phase across the edge, worn
|
||||||
|
// over the accent, more for iridescent tracks.
|
||||||
|
vec3 slick = 0.5 + 0.5 * cos(6.2831853 * (fres * 1.6 + vec3(0.0, 0.33, 0.67) + uTime * 0.05));
|
||||||
|
rim = mix(rim, rim * 0.4 + slick * length(rim) * 0.75, 0.25 + 0.5 * uIri);
|
||||||
|
// The fresnel per channel, red reaching in further than blue: a lens fringe.
|
||||||
|
float nv = 1.0 - max(dot(n, v), 0.0);
|
||||||
|
vec3 fresC = vec3(nv * nv * nv * mix(1.0, nv, -0.35), fres, nv * nv * nv * mix(1.0, nv, 0.35));
|
||||||
|
// A thin, hot edge that flares on hits.
|
||||||
|
float edge = fres * fres;
|
||||||
|
|
||||||
vec3 col = art * mix(1.0, 0.45, fres * uMorph);
|
vec3 col = art * mix(1.0, 0.45, fres * uMorph);
|
||||||
col += rim * fres * (0.5 + 0.7 * uKick + 0.4 * uDrop + 0.5 * uBass) * uMorph;
|
col += rim * fresC * (0.65 + 0.9 * uKick + 0.5 * uDrop + 0.5 * uBass) * uMorph;
|
||||||
|
col += mix(rim, vec3(1.0), 0.5) * edge * (0.3 + 1.2 * uKick + 0.8 * uDrop) * uMorph;
|
||||||
col += spec * uMorph;
|
col += spec * uMorph;
|
||||||
// Gloss: a dark room lit by the accent from above, and one soft window. They stay
|
// Gloss: a dark room lit by the accent from above, and one soft window. They stay
|
||||||
// put while the surface turns under them, which is what makes a spin read.
|
// put while the surface turns under them, which is what makes a spin read.
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ const sheets = {
|
|||||||
if ("serviceWorker" in navigator && import.meta.env.PROD) {
|
if ("serviceWorker" in navigator && import.meta.env.PROD) {
|
||||||
navigator.serviceWorker.register("/sw.js", { scope: "/" }).catch((err) => console.warn("no service worker:", err));
|
navigator.serviceWorker.register("/sw.js", { scope: "/" }).catch((err) => console.warn("no service worker:", err));
|
||||||
}
|
}
|
||||||
|
paintGrain();
|
||||||
|
|
||||||
const cards: Card[] = [];
|
const cards: Card[] = [];
|
||||||
let active = -1;
|
let active = -1;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
@@ -1172,6 +1174,24 @@ function signedIn(user: User, pl8list?: Pl8Status): void {
|
|||||||
if (!cards.length) openLinkOrFeed();
|
if (!cards.length) openLinkOrFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The tile of film grain the stylesheet lays under the page's content: faint
|
||||||
|
/// specks, each a little lighter or darker than what is under it.
|
||||||
|
function paintGrain(): void {
|
||||||
|
const size = 128;
|
||||||
|
const c = document.createElement("canvas");
|
||||||
|
c.width = c.height = size;
|
||||||
|
const ctx = c.getContext("2d");
|
||||||
|
if (!ctx) return;
|
||||||
|
const img = ctx.createImageData(size, size);
|
||||||
|
for (let i = 0; i < img.data.length; i += 4) {
|
||||||
|
const v = Math.random() < 0.5 ? 0 : 255;
|
||||||
|
img.data[i] = img.data[i + 1] = img.data[i + 2] = v;
|
||||||
|
img.data[i + 3] = Math.random() * 30;
|
||||||
|
}
|
||||||
|
ctx.putImageData(img, 0, 0);
|
||||||
|
document.documentElement.style.setProperty("--grain", `url(${c.toDataURL()})`);
|
||||||
|
}
|
||||||
|
|
||||||
/// `/?t=handle/slug` opens on that track; the feed carries on after it.
|
/// `/?t=handle/slug` opens on that track; the feed carries on after it.
|
||||||
async function openLinkOrFeed(): Promise<void> {
|
async function openLinkOrFeed(): Promise<void> {
|
||||||
const link = new URLSearchParams(location.search).get("t")?.split("/");
|
const link = new URLSearchParams(location.search).get("t")?.split("/");
|
||||||
|
|||||||
+14
-3
@@ -29,7 +29,9 @@
|
|||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--bg);
|
/* Film grain (main.ts paints the tile) under everything; cards lay their own
|
||||||
|
over their background, below their content. */
|
||||||
|
background: var(--grain, none), var(--bg);
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
font: 15px/1.4 var(--font);
|
font: 15px/1.4 var(--font);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -403,6 +405,15 @@ button.danger:not(.link) { background: var(--accent); border: none; color: #fff;
|
|||||||
background: radial-gradient(55% 40% at 50% 40%, color-mix(in oklab, var(--card-accent) 55%, transparent), transparent 70%);
|
background: radial-gradient(55% 40% at 50% 40%, color-mix(in oklab, var(--card-accent) 55%, transparent), transparent 70%);
|
||||||
opacity: calc(0.18 + var(--energy) * 0.55);
|
opacity: calc(0.18 + var(--energy) * 0.55);
|
||||||
}
|
}
|
||||||
|
/* Grain over the card's background only: content and actions sit above it. */
|
||||||
|
.card::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--grain, none);
|
||||||
|
}
|
||||||
|
.card > .content, .card > .actions { z-index: 1; }
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -456,8 +467,8 @@ button.danger:not(.link) { background: var(--accent); border: none; color: #fff;
|
|||||||
.card.liquid .art::before {
|
.card.liquid .art::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: -40%;
|
inset: calc(-50% - var(--energy) * 15%);
|
||||||
background: radial-gradient(closest-side, color-mix(in oklab, var(--card-accent) calc(20% + var(--energy) * 40%), transparent), transparent);
|
background: radial-gradient(closest-side, color-mix(in oklab, var(--card-accent) calc(28% + var(--energy) * 55%), transparent), transparent);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.card.liquid .content > :not(.art, .chrome-layer) { position: relative; z-index: 1; }
|
.card.liquid .content > :not(.art, .chrome-layer) { position: relative; z-index: 1; }
|
||||||
|
|||||||
Reference in new issue
Block a user