// Structural family: a receding skyline of towers with instanced lit windows. // // Distinct from Ridge Terrain (rolling fbm ridgelines) and Synthwave Run (a // ground grid): this is architecture, read as a front-on skyline. Heights and // footprints are per-tower noise, drawn far-to-near so a nearer tower correctly // occludes the one behind while a taller far tower still rises above a short // one in front. Windows are lit per facade cell, so the city has interior life // rather than reading as a lit cardboard cut-out. export const neonCity = { name: 'Neon City', family: 'structural', kind: 'fragment', // Personality: see look/Personality.js. consumes: ['ink'], traits: ['space', 'camera', 'style'], params: { cells: { type: 'float', range: [0.5, 4], default: 2.0, uniform: 'u_cells', bias: 'density' }, layers: { type: 'int', range: [2, 8], default: 6, uniform: 'u_layers', bias: 'density' }, height: { type: 'float', range: [0.2, 2.5], default: 1.0, uniform: 'u_height', bias: 'energy' }, speed: { type: 'float', range: [0.02, 0.4], default: 0.08, uniform: 'u_speed', bias: 'motion', rate: true }, horizon: { type: 'float', range: [-0.3, 0.3], default: 0.0, uniform: 'u_horizon' }, reflect: { type: 'float', range: [0, 1], default: 0.5, uniform: 'u_reflect' }, pulse: { type: 'float', range: [0, 1], default: 0.5, uniform: 'u_pulse' }, palette: { type: 'palette', count: 4 }, }, reactive: { height: { feature: 'bandLow', amount: 0.18, response: 'smooth' }, pulse: { feature: 'beat', amount: 0.2, response: 'smooth' }, }, shader: ` float towerEdge(vec2 grid, float mullions) { vec2 g = fract(grid); float steel = min(1.0 - smoothstep(0.0, 0.28 * mullions, g.x), 1.0 - smoothstep(0.0, 0.28 * mullions, g.y)); return steel; } vec4 scene(vec2 uv, vec2 p) { float t = u_time * u_speed + u_seed; float beat = u_beat; p = sigCamera(p); // Shares the track's ground line with every other scene that has one. float horizon = clamp(u_horizon + sigHorizonY() * 0.3, -0.9, 0.9); // Sky, darkening away from the horizon glow. vec3 sky = mix(pal(3) * 0.06, pal(0) * 0.35, smoothstep(-0.1, 0.7, p.y - horizon)); sky *= 0.4 + 0.6 * exp(-abs(p.y - horizon) * 3.0); vec3 col = sky; for (int i = 0; i < 8; i++) { if (i >= u_layers) break; float fi = float(i); float depth = float(i) / max(float(u_layers - 1), 1.0); // 0 far .. 1 near float parallax = mix(0.25, 1.0, depth); // Horizontal column coordinate for this depth ring. Receding layers // get more cells per screen and slide slower, giving parallax. float xsc = mix(1.2, 3.4, depth) * u_cells; float xc = p.x * xsc + t * 0.18 * parallax + fi * 31.7; float cx = floor(xc); float xf = fract(xc); float h1 = hash11(cx * 13.1 + fi * 7.3 + u_seed); float h2 = hash11(cx * 61.7 + fi * 5.9 + u_seed * 0.7); // Footprint half width, and the tower's top above its base. float footprint = (0.55 + h1 * 0.35) * 0.5; // in cell fracs float inTower = smoothstep(footprint + 0.04, footprint - 0.04, abs(xf - 0.5)); float baseY = horizon - (0.05 + depth * 0.9); float topY = baseY + u_height * (0.35 + h1 * 1.6) * mix(0.45, 1.0, depth); float body = step(p.y, topY) * step(baseY, p.y); float tower = inTower * body; if (tower < 0.01) continue; // Facade, tinted by how far into the screen the ring sits. float gy = clamp((p.y - baseY) / max(topY - baseY, 1e-4), 0.0, 1.0); vec3 facade = mix(pal(1), pal(3), depth); // Instanced window grid — each facade cell decides itself lit or dark, // with the pulse raising the lit population and the beat underlining it. vec2 g = vec2(xf * 5.0, gy * 9.0); vec2 cellg = floor(g); float lit = step(0.42, hash12(cellg + fi * 3.1)); float steel = 1.0 - min(smoothstep(0.0, 0.35, fract(g.x)), smoothstep(0.0, 0.35, fract(g.y))); vec3 windowCol = mix(pal(2), pal(3), 0.3); vec3 fut = mix(col, windowCol, lit * steel * (0.45 + 0.55 * u_pulse + beat * 0.25)); // Ground-couple: darker, breath where the tower meets the street. fut *= 0.7 + 0.3 * smoothstep(0.0, 0.5, gy); // Haze pushes the far rings into the sky, the way distance actually does. col = mix(col, fut, tower * (1.0 - 0.45 * (1.0 - depth))); } // Street: a slate floor with a molten reflection of the nearest towers. if (p.y < horizon) { float street = smoothstep(0.0, -0.6, p.y - horizon); col = mix(col, pal(3) * 0.08, street); float refl = exp(-abs(p.y - horizon) * 4.0) * u_reflect; col += pal(2) * refl * (0.08 + 0.2 * beat) * street; } // Horizon haze bloom, the city's glow pooling where towers meet the sky. col = sigAir(col, p, smoothstep(0.0, 1.4, length(p))); return vec4(inkValue(col), 1.0); } `, }; export default neonCity;