Turing Bloom runs activator-inhibitor in the feedback buffer, so the pattern is formed rather than drawn — the one organic here that will not go uniform. It declares no slow axis on purpose, and says why: its own convergence path moves the ten-second average by 0.10, and every candidate axis measured under that. Mycelium Web puts an organic on the ground instead of standing in front of the camera; the colony front is legible as area, which is also what makes its axis measurable. Scale Mosaic is a log-polar lattice with a shear, so the rows are spirals and every scale is the track's signature form. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.3 KiB
JavaScript
100 lines
4.3 KiB
JavaScript
// Organic family: a fungal mat seen in perspective, creeping toward the camera.
|
|
// Hyphae branch across the substrate, brighten where two threads cross, and the
|
|
// colony spreads outward from where it started.
|
|
//
|
|
// Flora grows upright, symmetric and toward the light; every organic scene in
|
|
// the library is a thing standing in a space. This one is a thing lying flat in
|
|
// one — a mat on the ground, receding to the track's horizon — so it reads as
|
|
// surface rather than as subject, and the branching is lateral and off-centre
|
|
// instead of radial.
|
|
|
|
export const myceliumWeb = {
|
|
name: 'Mycelium Web',
|
|
family: 'organic',
|
|
kind: 'fragment',
|
|
traits: ['camera', 'space', 'style'],
|
|
|
|
params: {
|
|
threads: { type: 'float', range: [1.5, 9], default: 3.5, uniform: 'u_threads', bias: 'density' },
|
|
branch: { type: 'float', range: [0, 1.4], default: 0.6, uniform: 'u_branch' },
|
|
fine: { type: 'float', range: [0.02, 0.35], default: 0.18, uniform: 'u_fine' },
|
|
front: { type: 'float', range: [0.4, 6], default: 1.6, uniform: 'u_front', slowAxis: true },
|
|
nodes: { type: 'float', range: [0, 1.4], default: 0.6, uniform: 'u_nodes', bias: 'energy' },
|
|
creep: { type: 'float', range: [0.01, 0.35], default: 0.07, uniform: 'u_creep', bias: 'motion', rate: true },
|
|
damp: { type: 'float', range: [0, 1], default: 0.5, uniform: 'u_damp' },
|
|
palette: { type: 'palette', count: 5 },
|
|
},
|
|
|
|
reactive: {
|
|
nodes: { feature: 'bandHigh', amount: 0.35, response: 'smooth' },
|
|
branch: { feature: 'bandMid', amount: 0.25, response: 'smooth' },
|
|
damp: { feature: 'loudness', amount: 0.2, response: 'smooth' },
|
|
},
|
|
|
|
shader: `
|
|
// Hyphae: ridged noise warped by itself, so threads run in bundles and fork
|
|
// rather than lying in a regular weave.
|
|
float hyphae(vec2 g, float scale, float warp) {
|
|
vec2 w = vec2(fbm(g * scale * 0.7 + 3.1, 3), fbm(g * scale * 0.7 - 7.4, 3)) - 0.5;
|
|
float n = vnoise(g * scale + w * warp * 3.0);
|
|
return 1.0 - abs(n * 2.0 - 1.0);
|
|
}
|
|
|
|
vec4 scene(vec2 uv, vec2 p) {
|
|
float t = u_time * u_creep + u_seed;
|
|
p = sigCamera(p);
|
|
|
|
float horizon = sigHorizonY() * 0.45 + 0.25;
|
|
float below = horizon - p.y;
|
|
|
|
// Air above the mat: nothing but the track's wash.
|
|
vec3 col = mix(pal(1) * 0.16, pal(0) * 0.06, sat((p.y - horizon) * 1.2 + 0.2));
|
|
|
|
if (below > 0.001) {
|
|
// Ground plane. Everything from here on is drawn in substrate
|
|
// coordinates, so the mat lies flat and the weave compresses with
|
|
// distance instead of being a flat texture pinned to the frame.
|
|
float depth = 1.0 / max(below, 0.02);
|
|
vec2 g = vec2(p.x * depth, depth) * 0.6;
|
|
g.y -= t * 2.0; // the colony creeps forward
|
|
|
|
float coarse = hyphae(g, u_threads, u_branch);
|
|
float fine = hyphae(g * 2.7 + 11.0, u_threads, u_branch * 0.6);
|
|
|
|
float w = u_fine * (0.4 + u_sigLine * 1.6);
|
|
float thread = smoothstep(w, w * 0.15, 1.0 - coarse);
|
|
thread += smoothstep(w * 0.6, 0.0, 1.0 - fine) * 0.5;
|
|
|
|
// The colony has a front: it is dense where it started and thins out at
|
|
// the edge of where it has reached.
|
|
float reach = smoothstep(u_front, u_front * 0.25, length(vec2(g.x, g.y + t * 2.0)) * 0.5);
|
|
thread *= mix(0.04, 1.0, reach);
|
|
|
|
// Anastomosis — where two hyphae meet and fuse. Those junctions are the
|
|
// only bright points in the mat, so the eye reads it as a network.
|
|
float node = sat(coarse * fine * 1.8 - 0.55) * reach;
|
|
|
|
vec3 mat = mix(pal(0) * 0.12, pal(2) * 0.95, sat(thread));
|
|
mat += pal(3) * sat(thread) * 0.22;
|
|
mat += palRamp(0.6 + node) * node * u_nodes * 1.6;
|
|
mat += pal(4) * sigEdge(1.0 - coarse - w) * u_nodes * 0.2;
|
|
|
|
// Wet substrate underneath, so the mat is not floating on black.
|
|
mat += pal(1) * 0.1 * (1.0 - sat(thread)) * u_damp * smoothstep(0.0, 0.6, below);
|
|
|
|
// Outside the front there is substrate and nothing else, so how far the
|
|
// colony has spread is legible as area rather than as brightness.
|
|
mat *= mix(0.3, 1.0, reach);
|
|
|
|
col = mix(col, mat, smoothstep(0.0, 0.04, below));
|
|
}
|
|
|
|
col = sigAir(col, p, sat(1.0 - below * 1.1));
|
|
col += sigGrain(uv);
|
|
return vec4(col, 1.0);
|
|
}
|
|
`,
|
|
};
|
|
|
|
export default myceliumWeb;
|