Nebelwand
Nebelwand is a hard-techno feature track in F minor at 148 BPM — and a
single TypeScript file. It’s the song you’ll see when you open
barline.dev, and it exercises a wide slice of the API: a
custom Pattern class, a sidechain pump, two send buses, eleven of the built-in
effects, several synth instruments,
every combinator, three flag kinds, and automation throughout. Nothing here sets
master effects, so it runs through the default master chain — glue → tilt → maximizer. This page walks its shape; the file itself is the canonical
reference.
The arrangement (192 bars ≈ 5:11)
Section titled “The arrangement (192 bars ≈ 5:11)”song.arrange([ { section: "intro" }, // 16 ▂ a filtered heartbeat { section: "build1" }, // 16 ▄ inherits intro, layers up { section: "drop1", repeat: 2 }, // 32 ▇ the full machine { section: "breakdown" }, // 16 ▃ kick gone, harmony forward { section: "build2" }, // 16 ▆ a rising wall built by a for-loop { section: "drop2", repeat: 2 }, // 32 █ new bass engine + acid { section: "peak", repeat: 2 }, // 32 █ everything, plus chords { section: "drop1" }, // 16 ▇ one last lap { section: "outro" }, // 16 ▂ the machine winds down]);The performance surface
Section titled “The performance surface”Three flags, one per kind, are the live controls:
song.flag("ratchet", { kind: "pad", note: 36, mode: "toggle" }); // hi-hat ratchet rollssong.flag("drive", { kind: "cc", cc: 1, range: [0, 1], smooth: 0.4 }); // mod wheel → energysong.flag("doubleKick", { kind: "constant", value: false }); // flip in code for 8th-note kicksBuses and the pump
Section titled “Buses and the pump”Two send buses give every track a shared space and echo instead of a reverb per track:
song.bus("space", { effects: [fx.reverb({ decay: 6, wet: 1 }), fx.eq3({ low: -10 })], volume: -6 });song.bus("echo", { effects: [fx.pingpong({ time: 0.75, feedback: 0.45, wet: 1 }), fx.filter({ frequency: 6500 })], volume: -8,});Every track names a built-in synth instrument by id —
kick, bass, hat, clap, metal, lead. The bass lives in the kick’s
shadow via a sidechain:
const rumble = song.track({ name: "rumble", output: { kind: "synth", synth: { id: "bass", params: { volume: -6 } } }, effects: [ fx.filter({ frequency: 700, type: "lowpass", q: 1.2 }), fx.sidechain({ source: "kick", amount: 0.8, release: 0.12 }), // the pump fx.compressor({ threshold: -20, ratio: 5 }), ],});The custom pattern
Section titled “The custom pattern”RatchetPattern is the file’s showcase of “Pattern is an open interface” — it
wraps offbeat hats and probabilistically explodes hits into rolls, reading the
drive flag live at query time and seeding ctx.rng per event so the piano
roll matches playback. The full class is in
Custom patterns.
const ratchetHats = new RatchetPattern(offHats, { probability: 0.2, divisions: 3, boostFlag: "drive", // knob up → more rolls, mid-bar});How sections build energy
Section titled “How sections build energy”A few techniques worth lifting:
inherit to layer. build1 re-applies the whole intro, then adds hats and
a bass with an opening filter sweep:
song.section("build1", bars(16), (t) => { t.inherit("intro"); hats.play(offHats).gain(0.7); hats.automate("gain", ramp(0.4, 1, bars(16))); rumble.automate("cutoff", sweep(250, 2500, bars(16), "exp")); // the classic opening filter clap.at(bars(8)).play(backbeat({ velocity: 0.6 })); // clap only from bar 8 of the section});Flags decide the details. drop1 reads the drive snapshot to scale gain
and gate a percussion layer, swaps the hats on the ratchet pad, and routes the
stabs through their own delay with .through():
song.section("drop1", bars(16), (t) => { const drive = Number(t.flags.drive ?? 0); kick.play(straightKick); hats.play(t.flags.ratchet === true ? ratchetHats : tickHats).gain(0.85); stab.play(stabsA).gain(0.5 + 0.3 * drive) .through(fx.delay({ time: 1.5, feedback: 0.3, wet: 0.25 })); // delay colors only the stabs if (drive > 0.5) perc.play(scatter).gain(drive * 0.8); // earn the knob if (Math.floor(t.position() / 4) === 15) hats.play(fast(2, crescRoll)).gain(0.5); // turnaround});A wall built by a loop. build2 is the strongest “it’s real TypeScript”
moment — a for loop stacks accelerating roll layers, each at(bars(n)) origin
looping to the section’s end so later layers pile on earlier ones:
song.section("build2", bars(16), (t) => { kick.play(at([0], 16)).gain(0.8); // one downbeat per bar — a pulse, not a groove rumble.automate("cutoff", sweep(400, 6000, bars(16), "exp")); for (let entry = 8; entry < 16; entry += 2) { clap.at(bars(entry)) .play(entry >= 14 ? fast(2, crescRoll) : crescRoll) // last layers double speed .gain(0.3 + (entry - 8) * 0.08); } if (Math.floor(t.position() / 4) >= 12) perc.play(scatterRolls).gain(0.6); // chaos for the run-in});Per-bar kick logic. drop2 chooses the kick pattern per bar from a flag and
the bar index:
const kickThisBar = t.flags.doubleKick === true ? doubleKick // 8th-note hammer mode : Math.floor(t.position() / 4) % 8 === 7 ? fillKick // every 8th bar gets a 32nd flam : straightKick;kick.play(kickThisBar);Read the source
Section titled “Read the source”That’s the tour. Every line in apps/studio/src/example/project.ts is annotated
in-file — open it next to the studio and edit while it plays. When the track
sounds right, bounce it down and check the levels in
Export & loudness. For smaller reusable fragments,
see Recipes.