Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335#!/usr/bin/env node// render-nullabye.mjs — a lullaby carved entirely out of cancelled noise.//// Technique study after Andy Brewer's "this song has no instruments in it"// (https://youtu.be/_Rk-hmIMv6I): take pink noise, duplicate it, invert the// phase of the copy — the sum is perfect silence. Put an EQ on the copy and// every deviation from flat breaks the cancellation; what you hear is the// *difference*. A narrow peaking bell at a note frequency becomes a breathy,// pitched voice pulled out of the hiss. The whole song is EQ automation —// there is no oscillator anywhere in this file.//// Honoring the original constraint: at most 24 EQ points (bands) exist.// A small allocator hands notes to free bands and refuses a 25th voice.// Pink noise + constant-Q bells = equal energy per voice at any pitch,// which is the quiet genius of the source video.//// Voices (all the same instrument — a bell in the veil):// pads — Q 28 chord tones, slow swells (the choir)// lead — Q 55 whistle melody, ±0.12% L/R detune (the whistler)// bass — Q 10 low roots, half-note pulse (the heartbeat root)// kick — Q 4.5 @ 54 Hz, 130 ms (the heartbeat)// hat — Q 5 @ 8.2 kHz ticks (the eyelash)// sparkle — Q 70 high offbeat glints (the mobile overhead)// veil — Q 0.8 sweep, the one moment the raw noise shows through//// Run:// node pop/nullabye/bin/render-nullabye.mjs// node pop/nullabye/bin/render-nullabye.mjs --out ~/nullabye.mp3// node pop/nullabye/bin/render-nullabye.mjs --proof # flat EQ ⇒ bit-exact silence//// 76 BPM 4/4 → beat ≈ 0.789 s, bar ≈ 3.158 s. 24 bars ≈ 76 s + tail.
import { writeFileSync, mkdirSync, unlinkSync } from "node:fs";import { dirname, resolve } from "node:path";import { fileURLToPath } from "node:url";import { homedir } from "node:os";import { spawnSync } from "node:child_process";
const HERE = dirname(fileURLToPath(import.meta.url));const SR = 48_000;const BLOCK = 64; // coefficient-update granularity (~1.3 ms)
const _argi = (k) => { const i = process.argv.indexOf(k); return i >= 0 ? process.argv[i + 1] : null; };const PROOF = process.argv.includes("--proof");
const BPM = 76;const BEAT = 60 / BPM;const BAR = 4 * BEAT;const TOTAL_BARS = 24;const totalSec = PROOF ? 6 : TOTAL_BARS * BAR + 3.0; // + tail for the last ringconst ns = Math.ceil(totalSec * SR);
const t = (bar, beat = 0) => bar * BAR + beat * BEAT;
// ── note table (Hz — the EQ thinks in frequency, so we do too) ────────const HZ = { F1: 43.65, A1: 55.0, C2: 65.41, F2: 87.31, G2: 98.0, A2: 110.0, F3: 174.61, G3: 196.0, A3: 220.0, B3: 246.94, C4: 261.63, D4: 293.66, E4: 329.63, G4: 392.0, A4: 440.0, B4: 493.88, C5: 523.25, D5: 587.33, E5: 659.26, G5: 783.99, A5: 880.0, C6: 1046.5, D6: 1174.66, E6: 1318.51, G6: 1567.98,};
// ── the 24-band pool ──────────────────────────────────────────────────const MAX_BANDS = 24;const GUARD = 0.15; // s between notes on the same band (let the ring drain)const bands = []; // { freeAt, events: [...] }let peakConcurrent = 0;
function note(t0, { freq, freqEnd = null, peakDb, q, atk, hold, rel }) { if (PROOF) peakDb = 0; // flat EQ — the silence proof const end = t0 + atk + hold + rel; let band = null; for (const b of bands) if (b.freeAt <= t0) { band = b; break; } if (!band) { if (bands.length >= MAX_BANDS) throw new Error(`25th EQ point requested at t=${t0.toFixed(2)}s — the video only had 24, so do we`); band = { freeAt: 0, events: [] }; bands.push(band); } band.freeAt = end + GUARD; band.events.push({ t0, freq, freqEnd, peakDb, q, atk, hold, rel, end }); const live = bands.filter((b) => b.events.some((e) => e.t0 <= t0 && t0 < e.end)).length; if (live > peakConcurrent) peakConcurrent = live;}
// envelope in dB-space: linear attack, hold, linear release (sounds exp)function envOf(e, time) { const dt = time - e.t0; if (dt < 0 || dt >= e.atk + e.hold + e.rel) return 0; if (dt < e.atk) return dt / e.atk; if (dt < e.atk + e.hold) return 1; return 1 - (dt - e.atk - e.hold) / e.rel;}
// ── score ─────────────────────────────────────────────────────────────// C major lullaby. Chord cycle C / Am / F / G, one bar each.const CHORDS = [ [HZ.C4, HZ.E4, HZ.G4, HZ.D5], // Cmaj9 [HZ.A3, HZ.C4, HZ.E4, HZ.B4], // Am9 [HZ.F3, HZ.A3, HZ.C4, HZ.G4], // Fmaj9 [HZ.G3, HZ.B3, HZ.D4, HZ.A4], // Gadd9];const BASS_ROOTS = [HZ.C2, HZ.A1, HZ.F2, HZ.G2];
const pad = (bar, freq, db = 20) => note(t(bar), { freq, peakDb: db, q: 28, atk: 0.8, hold: BAR - 2.0, rel: 1.2 });const lead = (bar, beat, freq, beats, db = 26) => note(t(bar, beat), { freq, peakDb: db, q: 55, atk: 0.06, hold: beats * BEAT - 0.06, rel: 0.35 });const bass = (bar, beat, freq) => note(t(bar, beat), { freq, peakDb: 26, q: 10, atk: 0.02, hold: 0.9 * BEAT, rel: 0.25 });const kick = (bar, beat, db = 30) => note(t(bar, beat), { freq: 54, peakDb: db, q: 4.5, atk: 0.004, hold: 0.02, rel: 0.13 });const hat = (bar, beat, db = 15) => note(t(bar, beat), { freq: 8200, peakDb: db, q: 5, atk: 0.003, hold: 0.01, rel: 0.07 });const sparkle = (bar, beat, freq, db = 20) => note(t(bar, beat), { freq, peakDb: db, q: 70, atk: 0.01, hold: 0.06, rel: 0.18 });
// intro — bars 0–3: the veil breathes. wide bells swell in and out of the// null so the listener hears the trick before they hear the song.const BREATHS = [[0, 420], [1, 640], [2, 960], [3, 1440]];for (const [bar, freq] of BREATHS) note(t(bar, 0.5), { freq, peakDb: 8.5, q: 0.9, atk: 1.1, hold: 0.3, rel: 1.3 });for (const f of CHORDS[0]) pad(3, f, 14); // first chord ghosts in early
// pads — bars 4–20, one chord per barfor (let bar = 4; bar <= 20; bar++) for (const f of CHORDS[bar % 4]) pad(bar, f, bar === 20 ? 17 : 20);
// melody — A section (bars 4–11)const PHRASE_A = [ [[HZ.E5, 0, 1.5], [HZ.G5, 1.5, 0.5], [HZ.A5, 2, 2]], [[HZ.G5, 0, 1], [HZ.E5, 1, 1], [HZ.D5, 2, 2]], [[HZ.C5, 0, 1.5], [HZ.D5, 1.5, 0.5], [HZ.E5, 2, 1], [HZ.G5, 3, 1]], [[HZ.D5, 0, 3]], [[HZ.E5, 0, 1.5], [HZ.G5, 1.5, 0.5], [HZ.A5, 2, 2]], [[HZ.G5, 0, 1], [HZ.A5, 1, 1], [HZ.C6, 2, 2]], [[HZ.A5, 0, 1.5], [HZ.G5, 1.5, 0.5], [HZ.E5, 2, 1], [HZ.C5, 3, 1]], [[HZ.D5, 0, 4]],];PHRASE_A.forEach((barNotes, i) => barNotes.forEach(([f, b, d]) => lead(4 + i, b, f, d)));
// melody — B section (bars 12–19): restate, then close upwardconst PHRASE_B = [ ...PHRASE_A.slice(0, 4), [[HZ.E5, 0, 1.5], [HZ.G5, 1.5, 0.5], [HZ.A5, 2, 2]], [[HZ.C6, 0, 2], [HZ.A5, 2, 2]], [[HZ.G5, 0, 1.5], [HZ.E5, 1.5, 0.5], [HZ.D5, 2, 1], [HZ.C5, 3, 1]], [[HZ.D5, 0, 2], [HZ.E5, 2, 2]], // hangs on E into the final C — home];PHRASE_B.forEach((barNotes, i) => barNotes.forEach(([f, b, d]) => lead(12 + i, b, f, d)));
// heartbeat — soft kick from bar 8, full from 12for (let bar = 8; bar <= 19; bar++) { kick(bar, 0, bar < 12 ? 26 : 30); kick(bar, 2, bar < 12 ? 24 : 28);}
// bass + hats + sparkles — B section onlyfor (let bar = 12; bar <= 19; bar++) { const root = BASS_ROOTS[bar % 4]; bass(bar, 0, root); bass(bar, 2, root); hat(bar, 1.5); hat(bar, 3.5, 13); const glint = [HZ.E6, HZ.G6, HZ.D6, HZ.E6][bar % 4]; sparkle(bar, bar % 2 ? 3.5 : 1.5, glint);}
// outro — bars 20–23: the veil lifts (one wide sweeping bell lets the raw// noise wash through), a last lone whistle, then the EQ goes flat and the// nulled noise swallows everything. the song ends in *literal* silence.note(t(20, 2), { freq: 250, freqEnd: 6000, peakDb: 9, q: 0.8, atk: 2.2, hold: 0.8, rel: 2.6 });lead(21, 0, HZ.E5, 2, 21);lead(22, 0, HZ.C5, 2, 17);
// ── render: two pink-noise tracks, one EQ'd + inverted, summed ────────// per channel: residue = chain(noise) − noise. flat chain ⇒ exact zero.for (const b of bands) b.events.sort((x, y) => x.t0 - y.t0);console.log(`→ nullabye · ${TOTAL_BARS} bars · C major @ ${BPM} BPM · ${bands.length}/${MAX_BANDS} EQ points · peak ${peakConcurrent} voices sounding`);
// deterministic noise (LCG → Paul Kellett pink filter)function makePink(seed) { let s = seed >>> 0; const rnd = () => ((s = (s * 1664525 + 1013904223) >>> 0) / 4294967296) * 2 - 1; let b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0, b6 = 0; return () => { const w = rnd(); b0 = 0.99886 * b0 + w * 0.0555179; b1 = 0.99332 * b1 + w * 0.0750759; b2 = 0.969 * b2 + w * 0.153852; b3 = 0.8665 * b3 + w * 0.3104856; b4 = 0.55 * b4 + w * 0.5329522; b5 = -0.7616 * b5 - w * 0.016898; const out = (b0 + b1 + b2 + b3 + b4 + b5 + b6 + w * 0.5362) * 0.11; b6 = w * 0.115926; return out; };}
const out = [new Float32Array(ns), new Float32Array(ns)];const DETUNE = [1.0012, 0.9988]; // ±0.12% L/R — the shimmer in the veil
for (let ch = 0; ch < 2; ch++) { const pink = makePink(ch ? 0xC0FFEE : 0xBEEF); const dry = new Float32Array(BLOCK); const wet = new Float32Array(BLOCK); // biquad state + per-band event cursor const st = bands.map(() => ({ z1: 0, z2: 0, cursor: 0, b0: 1, b1: 0, b2: 0, a1: 0, a2: 0 }));
for (let blockStart = 0; blockStart < ns; blockStart += BLOCK) { const n = Math.min(BLOCK, ns - blockStart); const time = blockStart / SR; for (let i = 0; i < n; i++) wet[i] = dry[i] = pink();
for (let bi = 0; bi < bands.length; bi++) { const band = bands[bi], s = st[bi]; // advance past finished events (keep 0.4 s to drain the ring) while (s.cursor < band.events.length && time > band.events[s.cursor].end + 0.4) { s.cursor++; s.z1 = 0; s.z2 = 0; } const e = band.events[s.cursor]; if (!e || time < e.t0 - 0.05) continue; // idle band: identity, skip
const env = envOf(e, time); const db = e.peakDb * env; const ringing = Math.abs(s.z1) + Math.abs(s.z2) > 1e-9; if (db <= 0.01 && !ringing) continue;
let freq = e.freq; if (e.freqEnd) { // exp glide across the event const p = Math.min(1, Math.max(0, (time - e.t0) / (e.end - e.t0))); freq = e.freq * Math.pow(e.freqEnd / e.freq, p); } // RBJ peaking EQ const A = Math.pow(10, db / 40); const w0 = (2 * Math.PI * Math.min(freq * DETUNE[ch], SR * 0.45)) / SR; const alpha = Math.sin(w0) / (2 * e.q); const cosw = Math.cos(w0); const a0 = 1 + alpha / A; s.b0 = (1 + alpha * A) / a0; s.b1 = (-2 * cosw) / a0; s.b2 = (1 - alpha * A) / a0; s.a1 = (-2 * cosw) / a0; s.a2 = (1 - alpha / A) / a0; // transposed direct form II over the block let { z1, z2 } = s; for (let i = 0; i < n; i++) { const x = wet[i]; const y = s.b0 * x + z1; z1 = s.b1 * x - s.a1 * y + z2; z2 = s.b2 * x - s.a2 * y; wet[i] = y; } s.z1 = z1; s.z2 = z2; }
// the trick itself: EQ'd copy minus the original. flat ⇒ silence. const o = out[ch]; for (let i = 0; i < n; i++) o[blockStart + i] = wet[i] - dry[i]; }}
// ── proof mode: flat EQ must null to bit-exact digital zero ───────────if (PROOF) { let peak = 0; for (const ch of out) for (let i = 0; i < ns; i++) peak = Math.max(peak, Math.abs(ch[i])); console.log(peak === 0 ? "✓ proof: all 24 bands flat → output is bit-exact silence (peak = 0)" : `✗ proof failed: peak = ${peak}`); process.exit(peak === 0 ? 0 : 1);}
// ── normalize + edge fades ────────────────────────────────────────────let peak = 0;for (const ch of out) for (let i = 0; i < ns; i++) peak = Math.max(peak, Math.abs(ch[i]));const g = peak > 0 ? 0.88 / peak : 1;const fadeIn = Math.floor(0.004 * SR), fadeOut = Math.floor(1.6 * SR);for (const ch of out) { for (let i = 0; i < ns; i++) ch[i] *= g; for (let i = 0; i < fadeIn; i++) ch[i] *= i / fadeIn; for (let i = 0; i < fadeOut; i++) ch[ns - 1 - i] *= i / fadeOut;}
// ── write out ─────────────────────────────────────────────────────────function expandHome(p) { if (!p) return p; if (p === "~") return homedir(); if (p.startsWith("~/")) return resolve(homedir(), p.slice(2)); return p;}const outPath = expandHome(_argi("--out")) || resolve(HERE, "..", "out", "nullabye.mp3");mkdirSync(dirname(outPath), { recursive: true });const rawPath = `${outPath}.f32.raw`;const buf = Buffer.alloc(ns * 2 * 4);for (let i = 0; i < ns; i++) { buf.writeFloatLE(out[0][i], i * 8); buf.writeFloatLE(out[1][i], i * 8 + 4);}writeFileSync(rawPath, buf);
// master chain — gentle: the material is all noise, so easy on the air band// and a soft knee so the breaths keep breathing.const MASTER = [ "highpass=f=30", "acompressor=threshold=-22dB:ratio=2.2:attack=25:release=220:makeup=2.0:knee=8", "treble=g=0.8:f=9000", "alimiter=limit=0.96:attack=5:release=80",].join(",");const ff = spawnSync("ffmpeg", ["-hide_banner", "-y", "-loglevel", "error", "-f", "f32le", "-ar", String(SR), "-ac", "2", "-i", rawPath, "-af", MASTER, "-c:a", "libmp3lame", "-q:a", "2", outPath], { stdio: "inherit" });try { unlinkSync(rawPath); } catch {}if (ff.status !== 0) { console.error("✗ ffmpeg failed"); process.exit(1); }console.log(`✓ ${outPath} (pop-mastered · ${(ns / SR).toFixed(1)} s)`);
// ── struct.json — section map for any future visualizer ───────────────{ const struct = { _comment: "Section map for nullabye (the cancelled-noise lullaby). 4/4, 76 BPM → bar ≈ 3.158 s. C major. Derived from render-nullabye.mjs.", meter: 4, bpm: BPM, scale: "major", rootMidi: 60, totalSec: +(ns / SR).toFixed(6), prerollSec: 0, sections: [ { name: "breaths", startSec: 0, endSec: +t(4).toFixed(6) }, { name: "lullaby-a", startSec: +t(4).toFixed(6), endSec: +t(12).toFixed(6) }, { name: "lullaby-b", startSec: +t(12).toFixed(6), endSec: +t(20).toFixed(6) }, { name: "veil-lifts", startSec: +t(20).toFixed(6), endSec: +(ns / SR).toFixed(6) }, ], }; writeFileSync(resolve(HERE, "..", "out", "nullabye.struct.json"), JSON.stringify(struct, null, 2) + "\n");}