Files
pl8list-rewind/web/src/blob/mesh.ts
T
nvrl 856ccb3b9a Replace loudness pulse with onset tracking and add liquid artwork visuals
The bass meter sat at full scale through every dubstep drop, so the glow froze.
features.ts now tracks band levels against their recent range, kick and snare
onsets from spectral flux, wobble in the mids, and drops, and the pulse follows
the kicks.

The playing card's artwork becomes a WebGL2 liquid drop that moves with those
features; tempo, tags and key set its character. Falls back to the plain artwork
without WebGL2, with reduced motion, or with visuals set to simple (v).
?debug=audio shows what the tracker hears.
2026-11-01 14:15:26 +01:00

47 lines
1.6 KiB
TypeScript

// Unit icosphere: an icosahedron with each triangle split into four, `levels` times.
// Level 5 has 10 242 vertices, which still fits 16-bit indices.
export interface Mesh {
positions: Float32Array<ArrayBuffer>;
indices: Uint16Array<ArrayBuffer>;
}
export function icosphere(levels: number): Mesh {
const t = (1 + Math.sqrt(5)) / 2;
const verts: number[][] = [
[-1, t, 0], [1, t, 0], [-1, -t, 0], [1, -t, 0],
[0, -1, t], [0, 1, t], [0, -1, -t], [0, 1, -t],
[t, 0, -1], [t, 0, 1], [-t, 0, -1], [-t, 0, 1],
].map(unit);
let faces = [
[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11],
[1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8],
[3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9],
[4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1],
];
for (let l = 0; l < levels; l++) {
// Each edge is split once, shared by the two faces on it.
const mids = new Map<string, number>();
const mid = (a: number, b: number) => {
const key = a < b ? `${a},${b}` : `${b},${a}`;
let i = mids.get(key);
if (i === undefined) {
const [p, q] = [verts[a], verts[b]];
i = verts.push(unit([p[0] + q[0], p[1] + q[1], p[2] + q[2]])) - 1;
mids.set(key, i);
}
return i;
};
faces = faces.flatMap(([a, b, c]) => {
const ab = mid(a, b), bc = mid(b, c), ca = mid(c, a);
return [[a, ab, ca], [b, bc, ab], [c, ca, bc], [ab, bc, ca]];
});
}
return { positions: new Float32Array(verts.flat()), indices: new Uint16Array(faces.flat()) };
}
function unit([x, y, z]: number[]): number[] {
const l = Math.hypot(x, y, z);
return [x / l, y / l, z / l];
}