LIGHTNING IN A BOTTLE — metal anthem, 150bpm, 64 bars as written; Jev arranges 7-16 sections. Spec: SPEC.md Start from STOPPED so the song begins at bar 1 (re-running it mid-play keeps the cycle count).
bars: 1-8 intro | 9-16 riff | 17-24 verse | 25-32 chorus | 33-40 build 41-48 drop | 49-56 drop2 | 57-64 outro (each mask = 64 steps) Where each section starts as written: the form (walk) and the text Jev is told (playsIn) both read it.
11const at = { intro: 0, riff: 8, verse: 16, chorus: 24, build: 32, drop: 40, drop2: 48, outro: 56 }
13const introBars = "<1!8 0!56>" 14const afterIntro = "<0!8 1!56>" 15const quoteOn = "<0 0 1 0!61>" 16const riffPedal = "<0!8 1!8 0!48>" 17const riffChord = "<0!16 1!16 0!24 1!4 0!4>" // verse, chorus, outro 18const riffBuild = "<0!32 1!8 0!24>" 19const riffHigh = "<0!24 1!16 0!24>" // chorus and build; the drop has its own 20const bassRoots = "<0!16 1!44 0!4>" 21const rock = "<0!8 1!16 0!8 1!8 0!16 1!4 0!4>" 22const chorus = "<0!24 1!8 0!32>" 23const hats = "<0!8 1!32 0!16 1!4 0!4>" 24const snare = "<0!8 1!52 0!4>" 25const drop = "<0!40 1!16 0!8>" 26const power = "<0!24 1!8 0!8 1!16 0!8>" 27const verse = "<0!16 1!8 0!40>" 28const outroVox = "<0!56 1 0!7>" 29const fills = "<0!7 1 0!56>" 30const roll39 = "<0!38 1 0!25>" 31const roll40 = "<0!39 1 0!24>" 32const riserBars = "<0!36 1!4 0!24>" 33const shotgun = "<0!8 1 0!23 1 0!7 1 0!23>" 34const crash = "<0!8 1 0!17 1 0 1 0 1 0!9 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0!9>" // 9, 27, 29, 31, the drop's chords 35const crashSoft = "<0!24 1 0!39>" // 25, under the sung "Th-" 36const final = "<0!60 1 0!3>"
Each part's masks, the one place that says where it plays: the parts below play from these, and the text Jev is told is derived from them.
a mask is on in a section if the pattern itself has an event there
48const onIn = (mask, from, to) => mask.queryArc(from, to).some((h) => h.value && Number(h.value) !== 0) 49const sectionNames = Object.keys(at) 50const sectionsOf = (masks) => sectionNames.filter((name) => 51 masks.some((m) => onIn(m, at[name], at[name] + 8))) 52const playsIn = (partName) => 'plays in: ' + sectionsOf(where[partName]).join(', ') 53const plays = (section) => 'plays: ' + Object.keys(where).filter((p) => sectionsOf(where[p]).includes(section)).join(', ')
── Jev: two of them, in TypeSafe's own shape (SPEC.md, "Jev in the booth") ── formJev picks the next section; djJev, asked once that is decided (questions in one request cannot see each other), says how much of each part plays it, how the song moves into it, and how hard. Every 8 bars. The form's rules (walk, at the bottom) narrow the sections Jev may pick and end the song; without Jev it plays as written.
60const song = { 61 title: 'LIGHTNING IN A BOTTLE', 62 what: 'a metal anthem on the Doom E1M1 riff, about using Jev properly: code decides what code can decide, Jev takes the rest', 63 tempo: '150 bpm, 4/4, 8 bars a section', 64 key: 'E minor', 65 chords: 'Em Em C C G G D D, two bars each, under the riff and the power chords', 66 lyrics: { 67 intro: 'We have lightning in a bottle, and yet it is not useful.', 68 verse: 'Remove the human from the loop / Routine calls never leave the machine / Only the residue goes to Jev / Forty-four out of forty-four', 69 chorus: 'The code decides what code can decide / Jev takes the rest', 70 outro: 'The code decides what code can decide', 71 }, 72}
74const formJev = jev({ 75 state: song, 76 questions: { 77 section: choice('Pick the section of the song that plays next, from those allowed. The outro ends the song: choose it once the song has built to its peak and made its point, before it drags', { 78 intro: 'a sub pulse and a plain robot voice reading the quote; ' + plays('intro'), 79 riff: 'the E1M1 riff on one note, the rock beat starts, a shotgun blast; ' + plays('riff'), 80 verse: 'the riff follows the chords under the vocoded verse: "Remove the human from the loop … Forty-four out of forty-four"; ' + plays('verse'), 81 chorus: 'the chorus: power chords, the riff an octave up, a fuller beat, the hook sung; ' + plays('chorus'), 82 build: 'the build: the riff\'s filter opening, a noise riser, a snare roll into the drop; ' + plays('build'), 83 drop: 'the drop, first half: sixteenth double kick, the guitars wide open, power chords, crashes; ' + plays('drop'), 84 drop2: 'the drop, second half, the same; ' + plays('drop2'), 85 outro: 'the last hook line, then the E power chord and a crash left to ring: the end; ' + plays('outro'), 86 }), 87 }, 88})
how much of each part the section decidedThisSection has: out, back or full
91const level = (part, what) => score('How much of ' + part + ' (' + what + '; ' + playsIn(part) + ') should the next section (decidedThisSection) have? Drop parts to build tension or make space, pull them back to let others speak, and bring them in full for the payoff; keep what defines the section', [ 92 'out: silent for the section', 93 'back: quieter than written', 94 'full: at its written level', 95]) 96const djJev = jev({ 97 state: song, 98 questions: { 99 riff: level('riff', 'the E1M1 riff, the song\'s signature'), 100 chords: level('chords', 'the power chords, the weight of the band'), 101 bass: level('bass', 'the sub on the chord roots, low'), 102 drums: level('drums', 'the drums, the crashes and the shotgun'), 103 vocals: level('vocals', 'the vocoded verse and chorus lines'), 104 into: choice('How should the song move into the next section (decidedThisSection)? It plays over the last bar of the section before it', { 105 cut: 'straight in, no transition', 106 fill: 'a drum fill down the toms into the first beat', 107 riser: 'white noise swelling up over the last bar', 108 dropout: 'everything stops for the last beat, a gap before the section hits', 109 }), 110 energy: score('How intense should the next section be? Shape the energy of the whole song, following its history, how it measured and how the listener reacted', [ 111 'held back: quieter', 112 'steady: a little under full', 113 'driving: full volume', 114 'all out: full volume, nothing held back', 115 ]), 116 }, 117})
a little chance in the form: the section is drawn from Jev's probabilities
120const { section } = formJev.every(8, { sample: 0.7 })
The build has its own riser, so the move into the drop is never a riser.
122const dj = djJev.every(8, { 123 after: { section }, 124 forget: ['into'], 125 allow: { into: ({ section: next }) => (next === 'drop' ? ['cut', 'fill', 'dropout'] : ['cut', 'fill', 'riser', 'dropout']) }, 126 fallback: { riff: 2, chords: 2, bass: 2, drums: 2, vocals: 2, into: 'cut', energy: 3 }, 127})
how the song moves into the next section, over the last bar of each
A part at Jev's level: silent when out, otherwise its velocity scaled by the level. The intro is not Jev's: it plays every part as written (level 2). Parts are composed inside the arrangement, so these are written bars. Each part has its own orbit, named for its question, so the meter hears it alone (measuredSound).
energy scales the parts together; the intro plays at full
144const energy = stack(pure(1).mask(introBars), dj.energy.score.div(3).range(.55, 1).mask(afterIntro))
E1M1 ("At Doom's Gate"), eighths, MIDI numbers: E2=40 E3=52 D3=50 C3=48 A#2=46 B2=47
147const riff = "<[40 40 52 40 40 50 40 40] [48 40 40 46 40 40 47 48] [40 40 52 40 40 50 40 40] [48 40 40 46@5]>"
two bars per chord: Em Em C C G G D D — the riff follows the root
a dropout's gap is silent: the section's last bar is cut short before its last beat, so the held A#2 does not ring through it
155const gapClip = stack(pure(1).mask("<1!7 0>"), into.fmap((c) => (c === 'dropout' ? .6 : 1)).mask("<0!7 1>"))
── parts ─────────────────────────────────────────
158const saw1 = (p) => p.s("sawtooth").decay(.14).sustain(.2).distort(1.5) 159const gtr = stack( 160 saw1(note(riff)).lpf(1500).gain(.35).mask(riffPedal), 161 saw1(note(riff).add(note(shift))).lpf(1500).gain(.35).mask(riffChord), 162 // the build: the filter opens from dull to bright across its 8 bars 163 saw1(note(riff).add(note(shift))).lpf(saw.slow(8).range(700, 3000)).gain(.35).mask(riffBuild), 164 // the drop: the guitars open up 165 saw1(note(riff).add(note(shift))).lpf(3000).gain(.35).mask(drop), 166 note(riff).add(note(shift)).add(note(12)).s("square").decay(.1).sustain(.1).lpf(2500).gain(.14).mask(riffHigh), 167 note(riff).add(note(shift)).add(note(12)).s("square").decay(.1).sustain(.1).lpf(3000).gain(.25).mask(drop), 168).clip(gapClip)
power chords, chorus and drop
sub: a pulse in the intro, then the chord roots, each with a triangle an octave up so small speakers hear it
176const sub = stack( 177 note("28*4").s("sine").decay(.35).sustain(0).gain(.7).mask(introBars), 178 note("40*4").s("triangle").decay(.35).sustain(0).lpf(300).gain(.25).mask(introBars), 179 note(roots).struct("x x x x").s("sine").decay(.3).sustain(.3).gain(.55).mask(bassRoots), 180 note(roots).add(note(12)).struct("x x x x").s("triangle").decay(.3).sustain(.3).lpf(250).gain(.2).mask(bassRoots), 181)
183const kit = stack( 184 s("bd:3").struct("x ~ ~ x x ~ ~ ~").gain(.6).mask(rock), 185 s("bd:3").struct("x ~ x x x ~ x ~").gain(.6).mask(chorus), 186 s("bd:3").struct("x*16").gain(.3).mask(drop), 187 s("808bd:1*4").gain(.2).mask(drop), 188 s("sn:3").struct("~ ~ x ~ ~ ~ x ~").room(.3).gain(.52).mask(snare), 189 s("hh*8").gain(".16 .08").mask(hats), 190 s("cr*4").gain(.25).mask(drop), // cr is a ride 191 // the intro's written fill: only when the move into the riff is a cut 192 s("ht ht mt mt lt lt lt lt").gain(.5).mask(fills).mask(into.fmap(onCutOr('cut'))), 193 // the build's roll, one crescendo over bars 39-40: only on a cut or a 194 // dropout (bar 39 looks two bars ahead for the drop's answer) 195 s("sn:3*16").gain(saw.slow(2).range(.16, .55)).mask(roll39).mask(dj.into.choice.early(2).fmap(onCutOr('cut', 'dropout'))), 196 s("sn:3*16").gain(saw.slow(2).range(.16, .55)).mask(roll40).mask(into.fmap(onCutOr('cut', 'dropout'))), 197 // the build's riser, bars 37-40 198 s("white*16").hpf(saw.slow(4).range(400, 8000)).gain(saw.slow(4).range(.03, .25)).decay(.07).sustain(0).mask(riserBars), 199 s("hh27:1").gain(3).mask(crash), // hh27:1 is the crash 200 s("hh27:1").gain(2.1).mask(crashSoft), // 3 dB down, under "Th-" 201 s("lbshotgun").gain(.85).mask(shotgun), 202) 203 204const vox = stack( 205 s("<lbremove ~ lbroutine ~ lbresidue ~ lbfortyfour ~>").gain(.85).room(.2).mask(verse), 206 s("<lbcodeem ~ lbcodec ~ lbrestg ~ lbrestd ~>").gain(.85).room(.25).mask(chorus), 207)
the story: the quote and the outro's hook line. Not Jev's: they always play.
the end: E power chord, crash, kick and shotgun, left to ring. It always plays.
216const ending = stack( 217 note("[e2,b2,e3]").s("sawtooth").decay(5.5).sustain(0).lpf(2000).distort(1.5) 218 .room(.7).size(.95).gain(.3).mask(final), 219 s("[bd:3,808bd:1]").room(.6).gain(.5).mask(final), 220 s("hh27:1").room(.6).gain(2.5).mask(final), 221 s("lbshotgun").gain(.85).mask(final), 222).mul(gain(.45)) // with the band: measured 6 dB over it at full, the master's peak
224const once = (p) => p.mask("<1!64 0!936>") // play once, then silence
── the form, as rules in code: which sections may follow which ──
at is where each starts as written; an empty next ends the song. The
written order is always allowed, and is what plays without Jev. One drop:
drop2 never leads back into it.
230const form = walk(section, { 231 max: 16, 232 sections: { 233 intro: { at: at.intro, next: ['riff'] }, 234 riff: { at: at.riff, next: ['verse', 'chorus'] }, 235 verse: { at: at.verse, next: ['chorus', 'riff'] }, 236 chorus: { at: at.chorus, next: ['build', 'verse'] }, 237 build: { at: at.build, next: ['drop'] }, 238 drop: { at: at.drop, next: ['drop2'] }, 239 drop2: { at: at.drop2, next: ['verse', 'chorus', 'outro'] }, 240 outro: { at: at.outro, next: [] }, 241 }, 242})
── the song: Jev's section, parts and energy, composed ── Each section plays its own bars, each part at the level Jev chose; the energy scales the parts' velocity together, so the mix keeps its balance. The story and the ending always play.
── into the next section: over the last bar of each, the next one's answer ── Past the last section there is no next one, so nothing plays there. The fill is the drums', at the level of the section it ends; this is played time, not written bars, so the intro (always the first 8) plays it full.
260const drumsNow = stack(pure(2).mask("<1!8 0!992>"), dj.drums.score.mask("<0!8 1!992>")) 261const transition = into.pick({ 262 cut: silence, 263 fill: s("ht ht mt mt lt lt [lt lt] [sn:3 sn:3]").gain(.5).orbit('drums') 264 .mask(drumsNow.round().gt(0)).mul(velocity(drumsNow.round().div(2))), 265 riser: s("white*16").hpf(saw.range(400, 8000)).gain(saw.range(.05, .35)).decay(.07).sustain(0).orbit('riser'), 266 dropout: silence, 267}).mask("<0!7 1>")
dropout: the parts stop for the section's last beat. The outro has no next section, so its last beat plays.