mirror of
https://github.com/narrrl/pl8list-rewind.git
synced 2026-11-04 08:46:59 +01:00
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.
47 lines
1.6 KiB
TypeScript
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];
|
|
}
|