Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313#!/usr/bin/env node// render-teknull.mjs — fast techno carved from cancelled noise, on a// strict 32-EQ-point budget. Composition lives here; ALL DSP runs in the// shared C engine (pop/nullabye/c/nullnoise.c) — this script just bakes// the score and calls run-c.mjs with the techno master preset.//// The trick (after Andy Brewer, https://youtu.be/_Rk-hmIMv6I): two copies// of pink noise, one phase-inverted — silence — and every sound is a// peaking EQ bell breaking the cancellation. Techno turns out to be the// technique's home turf:// · the CLAP is literally a burst of the noise itself (wide 2.3k bell)// · the ACID LINE is one monophonic EQ point — 303 slides are freqEnd// glides, accents are gain, note-off is the mono-cut// · the KICK is the classic 115→44 Hz pitch-drop, carved not synthesized//// 140 BPM 4/4 → bar = 240/140 s, so 70 bars = 120.0 s exactly: 2:00.//// bars section what runs// 0–7 intro kick, hats join, clap at 6, veil throughout// 8–15 build + offbeat sub, drone, perc, riser// 16–23 acid + the 303 line, working the room// 24–39 drop A + stabs, rim ghosts, air; lead hook from 32// 40–47 breakdown kick out — chord swells, noise-wash, subdrop boom// 48–63 drop B everything + ride 16ths + acid echo an octave up// 64–69 outro strip to kick + hats, final boom, veil closes//// Run:// node pop/teknull/bin/render-teknull.mjs # → out/teknull.mp3// node pop/teknull/bin/render-teknull.mjs --bake-only
import { writeFileSync, mkdirSync } from "node:fs";import { spawnSync } from "node:child_process";import { dirname, resolve } from "node:path";import { fileURLToPath } from "node:url";
const HERE = dirname(fileURLToPath(import.meta.url));const SR = 48_000;const BPM = 140;const BEAT = 60 / BPM;const BAR = 4 * BEAT;const TOTAL_BARS = 70; // 70 × 240/140 = 120.0 s — exactly 2:00const totalSec = TOTAL_BARS * BAR;
const t = (bar, beat = 0) => bar * BAR + beat * BEAT;const st = (semis) => 110 * Math.pow(2, semis / 12); // A2 = 110 Hz base
// sectionsconst DROP_A = (b) => b >= 24 && b <= 39;const BREAK = (b) => b >= 40 && b <= 47;const DROP_B = (b) => b >= 48 && b <= 63;const DROP = (b) => DROP_A(b) || DROP_B(b);const OUTRO = (b) => b >= 64;
// ── 32-point budget: every lane declared up front, audited below ──────const MAX_POINTS = 32;const lanes = new Map(); // name → { events: [] }
function note(lane, t0, { freq, freqEnd = 0, peakDb, q, atk, hold, rel, group = 0 }) { let L = lanes.get(lane); if (!L) { L = { events: [], group }; lanes.set(lane, L); } const prev = L.events[L.events.length - 1]; if (prev && prev.end > t0) { prev.cut = t0; prev.end = t0; } // mono-clip L.events.push({ t0, freq, freqEnd, peakDb, q, atk, hold, rel, end: t0 + atk + hold + rel, cut: -1 });}
// ── groove machinery ──────────────────────────────────────────────────// SWING: odd 16ths land late. EAGER hats push early, claps drag late// (the hellsine "eager percussion" trick). JITTER: deterministic ±4 ms// human wobble on everything that isn't the kick — the kick is the law.const SW = 0.08; // beats of swing on odd 16ths (~34 ms at 140)const sw = (beat) => (Math.round(beat * 4) % 2 ? beat + SW : beat);const jit = (seedA, seedB, amt = 0.004) => { let s = (seedA * 374761393 + seedB * 668265263 + 1442695041) >>> 0; s = (s ^ (s >> 13)) * 1274126177 >>> 0; return ((s / 4294967296) * 2 - 1) * amt;};const EAGER = -0.008, DRAG = 0.007; // seconds: hats lean in, claps lean back
// ── the score ─────────────────────────────────────────────────────────// A natural minor. Stabs on Am (A3 C4 E4 A4); acid on A2.const STAB = [220, 261.63, 329.63];const STAB_RHYTHM = [[0.75, 1.75, 3.25], [0.5, 2.25, 3.75]]; // alternates by bar
// acid pattern: 16 steps × [semitones from A2 | null=rest, accent, slideToNext]// the RESTS are the groove — a 303 line has to breatheconst ACID = [ [0, 1, 0], [null, 0, 0], [0, 0, 0], [12, 0, 1], [null, 0, 0], [0, 1, 0], [null, 0, 0], [7, 0, 0], [null, 0, 0], [0, 0, 0], [3, 0, 1], [null, 0, 0], [0, 1, 0], [null, 0, 0], [10, 0, 1], [12, 0, 0],];const ACID_FILL = [ // every 4th bar climbs out the top [0, 1, 0], [0, 0, 0], [12, 0, 0], [0, 0, 0], [3, 1, 0], [7, 0, 1], [10, 0, 0], [12, 0, 1], [15, 1, 0], [12, 0, 0], [10, 0, 0], [7, 0, 0], [3, 0, 1], [0, 1, 0], [-2, 0, 1], [0, 0, 0],];
for (let bar = 0; bar < TOTAL_BARS; bar++) { const inBreak = BREAK(bar); const drop = DROP(bar);
// kick — four on the floor, out for the breakdown. two stacked bells // per hit: the click-thump and a SUB BODY underneath (52→40) with a // long tail — at 40 Hz a cycle is 25 ms, the boom needs time to bloom. // SOFT from the very start: the intro opens at +22 dB with a slow // attack and a duller sweep, growing to the full machine thump by bar 8. if (!inBreak && bar <= 67) for (const beat of [0, 1, 2, 3]) { const soft = bar < 8 ? bar / 8 : 1; note("kick", t(bar, beat), { freq: 80 + soft * 35, freqEnd: 38, peakDb: 22 + soft * 9, q: 2.2, atk: 0.002 + (1 - soft) * 0.012, hold: 0.02, rel: 0.32, }); if (bar >= 4) note("kickSub", t(bar, beat + 0.025), { freq: 52, freqEnd: 40, peakDb: 26 * Math.min(1, soft + 0.3), q: 4.5, atk: 0.008, hold: 0.08, rel: 0.26 }); }
// hats — eager (lean in early), accent cycle, open hat wanders if (bar <= 67) // ticking from bar 0 — soft at first, full by bar 8 [0.5, 1.5, 2.5, 3.5].forEach((beat, i) => note("hatC", t(bar, beat) + EAGER + jit(bar, i), { freq: 9200, peakDb: (bar < 8 ? -3 : 0) + [22, 19, 23, 20][i], q: 2.5, atk: 0.002, hold: 0.006, rel: 0.04, })); if (bar >= 4 && bar <= 67 && !inBreak) note("hatO", t(bar, bar % 4 === 2 ? 1.5 : 3.5) + EAGER, { freq: 7400, peakDb: 20, q: 1.8, atk: 0.002, hold: 0.015, rel: 0.2 });
// clap — 2 and 4, dragged late against the eager hats; ghost "clap-a" // closing every 4th bar. the most honest instrument here, it IS the noise if (bar >= 6 && bar <= 67 && !inBreak) { for (const beat of [1, 3]) note("clap", t(bar, beat) + DRAG, { freq: 2300, peakDb: 21, q: 1.3, atk: 0.002, hold: 0.03, rel: 0.15 }); if (bar % 4 === 3) note("clap", t(bar, 3.375) + DRAG, { freq: 2300, peakDb: 15, q: 1.3, atk: 0.002, hold: 0.02, rel: 0.1 }); }
// sub — offbeat "oontz" with a small downward settle per note; odd bars // anticipate beat 3 by a 16th — that little push IS the groove if (bar >= 8 && bar <= 67 && !inBreak) for (const beat of bar % 2 ? [0.5, 1.5, 2.75, 3.5] : [0.5, 1.5, 2.5, 3.5]) note("sub", t(bar, beat), { freq: 56, freqEnd: 49, peakDb: 26, q: 6, atk: 0.01, hold: 0.16, rel: 0.16 });
// acid — ONE monophonic EQ point doing 303 work, now swung + jittered if ((bar >= 16 && bar <= 39) || DROP_B(bar)) { const pat = bar % 4 === 3 ? ACID_FILL : ACID; for (let k = 0; k < 16; k++) { const [semi, accent, slide] = pat[k]; if (semi === null) continue; const nxt = pat[(k + 1) % 16]; note("acid", t(bar, sw(k * 0.25)) + jit(bar * 16 + k, 7, 0.003), { freq: st(semi), freqEnd: slide && nxt[0] !== null ? st(nxt[0]) : 0, peakDb: accent ? 23 : 17, q: 22, atk: 0.008, hold: 0.05, rel: 0.06, }); } }
// acid2 — drop B echo, an octave up, 3 sixteenths behind, quieter if (DROP_B(bar)) { const pat = bar % 4 === 3 ? ACID_FILL : ACID; for (let k = 0; k < 16; k++) { const [semi, , ] = pat[k]; if (semi === null) continue; note("acid2", t(bar, sw(k * 0.25 + 0.75)) + jit(bar * 16 + k, 13, 0.003), { freq: st(semi + 12), peakDb: 13, q: 30, atk: 0.008, hold: 0.04, rel: 0.05 }); } }
// stabs — Am hits, rhythm alternates by bar, riding the swing if (drop) { for (const beat of STAB_RHYTHM[bar % 2]) { STAB.forEach((f, i) => note(`stab${i}`, t(bar, sw(beat)), { freq: f, peakDb: 17, q: 40, atk: 0.006, hold: 0.07, rel: 0.12 })); note("stabOct", t(bar, sw(beat)), { freq: 440, peakDb: 14, q: 40, atk: 0.006, hold: 0.07, rel: 0.12 }); } }
// percussion spice — swung if (bar >= 8 && bar <= 63) note("perc1", t(bar, sw(bar % 2 ? 3.75 : 1.25)) + jit(bar, 23), { freq: 380, freqEnd: 200, peakDb: 16, q: 5, atk: 0.003, hold: 0.02, rel: 0.09 }); if (drop) for (const beat of [2.75, 3.25]) note("perc2", t(bar, sw(beat)) + jit(bar * 4 + beat, 31), { freq: 4600, peakDb: 14, q: 9, atk: 0.002, hold: 0.008, rel: 0.05 });
// ride 16ths — drop B sizzle, swung, with a hole at the bar's last 16th if (DROP_B(bar)) for (let k = 0; k < 16; k++) { if (k % 8 === 7) continue; note("ride16", t(bar, sw(k * 0.25)) + jit(bar * 16 + k, 41, 0.002), { freq: 12500, peakDb: k % 4 === 2 ? 12 : 9, q: 5, atk: 0.002, hold: 0.005, rel: 0.04 }); }
// LEADS — the hook rides the WHOLE drop now: a swung 2-bar pentatonic // riff, lifting a fifth in each drop's back half, octave echo trailing if (drop) { const riff = bar % 2 === 0 ? [[440, 0.75, 0.5], [523.25, 1.5, 0.25], [659.26, 2.25, 0.75], [587.33, 3.25, 0.5]] : [[659.26, 0.5, 0.5], [587.33, 1.25, 0.25], [523.25, 1.75, 0.5], [440, 2.5, 1.25]]; const lift = (DROP_A(bar) && bar >= 32) || (DROP_B(bar) && bar >= 56) ? 1.5 : 1; for (const [f, beat, beats] of riff) { const t0 = t(bar, sw(beat)) + jit(bar * 7 + beat * 4, 99, 0.003); note("lead", t0, { freq: f * lift, peakDb: 22, q: 35, atk: 0.015, hold: beats * BEAT - 0.02, rel: 0.18 }); note("lead2", t0 + 0.75 * BEAT, { freq: f * lift * 2, peakDb: 13, q: 45, atk: 0.02, hold: beats * BEAT * 0.5, rel: 0.12 }); } }
// breakdown — chord swells + rising noise-wash while the kick rests if (inBreak) { note("noisewash", t(bar), { freq: 1200 + (bar - 40) * 350, peakDb: 9 + (bar - 40) * 0.7, q: 0.5, atk: 0.5, hold: BAR - 0.5, rel: 0.8 }); }}
// one-shots: crashes, risers, drop zaps, sub boomsfor (const bar of [8, 16, 24, 32, 40, 48, 56, 64]) note("crash", t(bar), { freq: 5500, peakDb: 16, q: 0.7, atk: 0.003, hold: 0.05, rel: 1.0 });for (const bar of [14, 22, 46, 62]) note("riser", t(bar), { freq: 600, freqEnd: 6500, peakDb: 8.5, q: 1.2, atk: 2 * BAR - 0.6, hold: 0.3, rel: 0.3 });for (const bar of [24, 48, 64]) note("fall", t(bar), { freq: 3500, freqEnd: 90, peakDb: 18, q: 2, atk: 0.004, hold: 0.05, rel: 0.45 });for (const bar of [40, 64, 68]) note("subdrop", t(bar), { freq: 55, freqEnd: 30, peakDb: 24, q: 3, atk: 0.01, hold: 0.4, rel: 2.0 });
// breakdown chord swells — ONE long event per lane, not per-barSTAB.forEach((f, i) => note(`swell${i}`, t(40), { freq: f, peakDb: 20, q: 28, atk: 2.0, hold: 8 * BAR - 3.5, rel: 1.5, group: 1 }));
// ── HARMONY CHANNEL (group 1) — a SECOND cancellation pair ────────────// independent pink noise + its own serial chain, residues summed in// parallel with the main channel. that means these chords can sit on the// SAME frequencies the stabs/leads already boost — no serial dB blowup.// voice-led Am / F / C / G, two bars per chord: real harmonic motion.{ const VOICED = { Am: [220, 261.63, 329.63], // A3 C4 E4 F: [220, 261.63, 349.23], // A3 C4 F4 C: [196, 261.63, 329.63], // G3 C4 E4 G: [196, 246.94, 293.66], // G3 B3 D4 }; const PROG_BUILD = ["Am", "F"]; // bars 16–23: gentle sway const PROG_DROP = ["Am", "F", "C", "G"]; // drops: the full turn for (let bar = 16; bar < 64; bar += 2) { if (BREAK(bar)) continue; // the swells own the breakdown const drop = DROP(bar); if (!drop && bar >= 24) continue; const prog = drop ? PROG_DROP : PROG_BUILD; const chord = VOICED[prog[Math.floor((bar - (drop ? 24 : 16)) / 2) % prog.length]]; chord.forEach((f, i) => note(`harmPad${i}`, t(bar), { freq: f, peakDb: drop ? 16 : 13, q: 26, atk: 0.5, hold: 2 * BAR - 1.3, rel: 0.8, group: 1 })); note("harmHi", t(bar), { freq: chord[2] * 2, peakDb: drop ? 12 : 9, q: 30, atk: 0.6, hold: 2 * BAR - 1.4, rel: 0.8, group: 1 }); }}
// the NEVER-SILENT floor — veil (mids) + drone (low) + air (high), as// 8-BAR events: per-bar bed events left a mono-cut hole at every bar// line (the loudness curve notched −35 dB at each seam, 70 times). With// 8-bar spans the only seams sit on crash-marked section starts.for (const [a, z] of [[0, 8], [8, 16], [16, 24], [24, 32], [32, 40], [40, 48], [48, 56], [56, 64], [64, 70]]) { const dropSect = DROP(a); const span = (z - a) * BAR; note("veil", t(a), { freq: 900, peakDb: dropSect ? 7 : 6.5, q: 0.5, atk: 0.3, hold: span - 0.3, rel: 0.6 }); note("drone", t(a), { freq: 110, peakDb: a >= 8 ? 13 : 11, q: 12, atk: 0.4, hold: span - 0.4, rel: 0.6 }); note("air", t(a), { freq: 13000, peakDb: dropSect ? 6 : 4.5, q: 1.0, atk: 0.5, hold: span - 0.5, rel: 0.5 });}
// ── audit the budget ──────────────────────────────────────────────────if (lanes.size > MAX_POINTS) throw new Error(`${lanes.size} EQ points exceeds the ${MAX_POINTS}-point budget`);console.log(`→ teknull · ${TOTAL_BARS} bars · A minor @ ${BPM} BPM · ${lanes.size}/${MAX_POINTS} EQ points`);
// ── bake the score for the C engine ───────────────────────────────────const bands = [...lanes.values()];for (const b of bands) b.events.sort((x, y) => x.t0 - y.t0);
// loudness ride: techno wants a firm floor — flat-ish targets, dip for// the breakdown, lift for the dropsconst rideTarget = (bar) => { if (BREAK(bar)) return -17.5; if (DROP(bar)) return -16.6; if (OUTRO(bar)) return -19; if (bar >= 16) return -18; if (bar >= 8) return -18.5; return -19.5;};const RIDE_BARS = 68; // leave the final boom + veil tail alone
const L = [];L.push(`sr ${SR}`, `dur ${totalSec}`, `detune 1.0009 0.9991`, `seed ${0xAC1D} ${0x303}`);L.push(`normpeak 0.9`, `fadein 0.004`, `fadeout 1.2`);L.push(`ridewin ${BAR} ${RIDE_BARS}`);for (let b = 0; b < RIDE_BARS; b++) L.push(String(rideTarget(b)));for (const g of [0, 1]) { const groupBands = bands.filter((b) => (b.group ?? 0) === g); if (!groupBands.length) continue; if (g > 0) L.push(`group ${0x7E77 + g} ${0x9A11 + g}`); // fresh noise pair per group for (const band of groupBands) { L.push(`band ${band.events.length}`); for (const e of band.events) L.push([e.t0, e.freq, e.freqEnd, e.peakDb, e.q, e.atk, e.hold, e.rel, e.cut].join(" ")); }}const OUT_DIR = resolve(HERE, "..", "out");mkdirSync(OUT_DIR, { recursive: true });const scorePath = resolve(OUT_DIR, "teknull.score.txt");writeFileSync(scorePath, L.join("\n") + "\n");console.log(`✓ baked ${bands.length} bands → ${scorePath}`);if (process.argv.includes("--bake-only")) process.exit(0);
// ── render through the C engine + techno master ───────────────────────const _argi = (k) => { const i = process.argv.indexOf(k); return i >= 0 ? process.argv[i + 1] : null; };const outPath = _argi("--out") || resolve(OUT_DIR, "teknull.mp3");const r = spawnSync("node", [resolve(HERE, "../../nullabye/c/run-c.mjs"), scorePath, "--out", outPath, "--master", "techno"], { stdio: "inherit" });process.exit(r.status ?? 1);