// creation of soundtrack
// 33956 Hz chosen as sampling rate to have a power of two (8192) as note length
A = new AudioContext({sampleRate:33956});
// 4e6 = 4 000 000 = 15 patterns * 32 notes each * 8192 samples, plus a little extra to decay the last note
B = A.createBuffer(1,4e6,33956);
C = B.getChannelData(0);
v = [z=-1]; // buffer for delay line (length y)
// setting z to -1 as a shortcut to initialize t,e,z at the first execution of the main loop
for (i=0; i<4e6; C[++i] =v[x%y] /*+ .3*snare + .3*drum*/) {
// Blood Money ingame
// pattern = 32 notes = 7.7s
// main instrument : volume increases with time, harmonics 1 (100%) and 2 (30 to 40%)
// 7 (removed in demo): a4,,a4,,a4,,a4,g4,,f4,,e4,,d4,e4,f4,g4,,g4,f4,g4,,f4,e4,,e4,,e4,,f4,,g4,
// 5 : a4,,a4,,a4,,a4,g4,,f4,,e4,,f4,,,g4,,g4,,g4,,f4,e4,,e4,,e4,e4,e4,f4,f4,
// 3 : f4,,e4,,d4,,c4,d4,,,,,,,c4,,d4,,,,e4,,f4,e4,,,,,,,,,
// 5 : a4,,a4,,a4,,a4,g4,,f4,,e4,,f4,,,g4,,g4,,g4,,f4,e4,,e4,,e4,e4,e4,f4,f4,
// 3 : f4,,e4,,d4,,c4,d4,,,,,,,c4,,d4,,,,e4,,f4,e4,,,,,,,,,
// 9 : e4,f4,e4,a4,e4,a4,e4,a4,,f4,e4,f4,,e4,,f4,g4,f4,e4,g4,f4,e4,d4,e4,,e4,,,,,,,
// 8 : e4,f4,e4,a4,e4,f4,e4,a4,,f4,e4,f4,,e4,,f4,g4,f4,e4,g4,f4,e4,d4,e4,,,,,,f4,,e4,
// 10 : a4,,a4,,a4,,a4,g4,,f4,,,g4,f4,g4,a4,g4,,g4,f4,g4,,f4,e4,,,,,e4,e4,f4,f4,
// 10 : a4,,a4,,a4,,a4,g4,,f4,,,g4,f4,g4,a4,g4,,g4,f4,g4,,f4,e4,,,,,e4,e4,f4,f4,
// 3 : f4,,e4,,d4,,c4,d4,,,,,,,c4,,d4,,,,e4,,f4,e4,,,,,,,,,
// 9 : e4,f4,e4,a4,e4,a4,e4,a4,,f4,e4,f4,,e4,,f4,g4,f4,e4,g4,f4,e4,d4,e4,,e4,,,,,,,
// 8 : e4,f4,e4,a4,e4,f4,e4,a4,,f4,e4,f4,,e4,,f4,g4,f4,e4,g4,f4,e4,d4,e4,,,,,,f4,,e4,
// 12 : a4,,,,,,,,f4,,,,,,,,,f4,e4,f4,g4,,,,,a4,g4,f4,g4,f4,e4,f4,
// 12 : a4,,,,,,,,f4,,,,,,,,,f4,e4,f4,g4,,,,,a4,g4,f4,g4,f4,e4,f4,
// 11 : f4,,e4,,d4,,c4,d4,,,,,,,,f4,g4,,f4,d4,e4,f4,e4,g4,,g4,f4,e4,e4,f4,e4,f4,
// 11 : f4,,e4,,d4,,c4,d4,,,,,,,,f4,g4,,f4,d4,e4,f4,e4,g4,,g4,f4,e4,e4,f4,e4,f4,
// silences encoded as "0" (charCode 48) => u==0
// access beyond the string size returns undefined, which also coerces as false in the expression below
u = "<0<0<0<:08070800:0:0:0870707778880705035000000305000708700000000<0<0<0<:08070800:0:0:0870707778880705035000000305000708700000000787<7<7<08780708:87:875707000000787<787<08780708:87:875700000807<0<0<0<:0800:8:<:0:8:08700007788<0<0<0<:0800:8:<:0:8:0870000778880705035000000305000708700000000787<7<7<08780708:87:875707000000787<787<08780708:87:875700000807<0000000800000000878:0000<:8:878<0000000800000000878:0000<:8:8788070503500000008:085787:0:8778788070503500000008:085787:0:877878".charCodeAt(i>>13)-48;
x = i&8191;
// Guitar sound simulated with Karplus-Strong algorithm
u && // if (u>0)
(y = 0|33956 / 55 / 2**(u/12)); // buffer length depends on current note
// Initialize the delay line buffer with x**2.2/11%1 as pseudorandom, which defines the timbre
// Then use a lowpass filter after
v[x%y] = u && y>x ? x**2.2/11%1 : .9*v[x%y]+.1*v[(x+1)%y];
// drum loop that did not make it in
//drum = (((i&32768?1e4:1e3)/(i&32767))&1);
//snare = ((Math.sqrt((i+32768)%65536)<<6)/255)%1;
}
w=A.createBufferSource();
w.buffer=B;
w.connect(A.destination);
// creation of shade bobs (used as brushes afterwards)
s = a.cloneNode();
cc = s.getContext("2d");
cc.fillStyle = r = cc.createRadialGradient(15, 15, 7, 15, 15, 15);
r.addColorStop(0, "#000c18"); // hue 210
r.addColorStop(1, "#000");
cc.fillRect(0,0,30,30);
cc.translate(30,0);
cc.fillStyle = r = cc.createRadialGradient(15, 15, 7, 15, 15, 15);
r.addColorStop(0, "#180c00"); // hue 30
r.addColorStop(1, "#000");
cc.fillRect(0,0,30,30);
cc.translate(30,0);
cc.fillStyle = r = cc.createRadialGradient(15, 15, 7, 15, 15, 15);
r.addColorStop(0, "#0c0018"); // hue 270
r.addColorStop(1, "#000");
cc.fillRect(0,0,30,30);
cc.translate(30,0);
cc.fillStyle = r = cc.createRadialGradient(15, 15, 7, 15, 15, 15);
r.addColorStop(0, "#0c1802"); // hue 93
r.addColorStop(1, "#000");
cc.fillRect(0,0,30,30);
cc.translate(30,0);
cc.fillStyle = r = cc.createRadialGradient(15, 15, 7, 15, 15, 15);
r.addColorStop(0, "#18000c"); // hue 330
r.addColorStop(1, "#000");
cc.fillRect(0,0,30,30);
cc.translate(30,0);
w.start();
setInterval( D => {
// synchronize with music, twice every pattern (16 notes, as one pattern is 32 notes)
// 33956/131072, round to .259
(A.currentTime*.259|0)>z && // if ((A.currentTime*.259|0)> z)
(e=1+2*++z,t=0);
c.globalCompositeOperation="source-over";
// repaint canvas and bobs every step so we can animate them. First clear the background, flashing it at every sync point
c.fillStyle="hsl(0,0%,"+Math.max(0,100-9*t)+"%)";
c.fillRect(0, 0, a.width, a.height);
c.globalCompositeOperation="lighter";
D=20+z%4*t/15; // bob size : zoom in
// Hopalong formula initialization
v = .99+.01*Math.sin(e);
u = Math.cos(1+7*e);
x=4;
y=.265;
for (i=0; i<2048 && ++i<32*t; c.drawImage(s, e%5*30, 0, 30, 30, (y+x)*D-D/2+.5*a.width, a.height/2+(y-x)*D-D/2, D, D))
// Hopalong formula
[x,y] = [y-Math.sign(x)*Math.abs(u*x+v)**.5, -x];
++e; // after 2048 bobs, change the color (e) and the parameters of the formula
D=40-z%4*t/15; // bob size : zoom out
v = .99+.01*Math.sin(e);
u = Math.cos(1+7*e);
x=4;
y=.265;
for (i;++i<32*t; c.drawImage(s, e%5*30, 0, 30, 30, (y+x)*D-D/2+.5*a.width, a.height/2+(y-x)*D-D/2, D,D))
// Hopalong formula
[x,y] = [y-Math.sign(x)*Math.abs(u*x+v)**.5, -x];
++t; --e;
}, 20)