Upload any image and listen to how it sounds. Procedural chaos provided as example.
for(_='=`qc[c.zzgC]q~t&3];"ile!entH4*G)}KreJ`;__B.onc^^lick=Zzfy]q#Y,0,X);](mX0=zgg](DX2,h&255)+inat=newda++=e=>{.onloadDe.now(zfc](=0;AA0Xw,+=[0,1X-1][Mh.*(s(2*PI*t)for( ii<w<<8;i)Yfff__b.sertBefoJ(B,aB.ta.play(U(d=D=-2)))E+=s[Gx+Gw*y=documH.cJeElemH`K;BbuttonextContHq; nn-1e5;t=13*t+u+r+9*cos(t)&-1){x"y3-" ii-6;) EE-6;)p[w*G(y+EG(x+iu]}u i c)c[i[0]+i[6]]=i;U~diffeJnce_if((D=()-d)>>4)<w){setTimeout(U,33K~source-over`};Tsh).;dqRIFFWAVEfmt `+ob(`EAEQCAPggD4AEAC`)+`_u=1;v=[];{x=i>>8; E=yy<h;y){t=i*11/800*pow(2,(h-67-y)/24]*(t%1+1]/2+1+2]>0?1:0Ku=max(u,Ev.push(EKd+=Strg.fromCharCode(0|1+v[i]*254/ua Audio(`:audio/wav;base64,`+btoa(d)};Cw=256;h=192;Y000_hmu=x=y=t=wp=m.;zpgr*=3.9G(1-rT(NewZC;A=Bputypeqf!^hangef F!Reader;fmimg_m.src=f.Jsult;mhw=m.width;h=m(192,m.heightzdaT(K};f.JadAsDaURL(A.f!s[0]PlayZe=>;C(r=.1)';G=/[-X-Z^_JKGH!"~zq]/.exec(_);)with(_.split(G))_=join(shift());eval(_)
Zm9yKF89Jz1gcWNbYy56emdDXXF+dCYzXTsiaWxlIWVudEg0KkcpfUtyZUpgO19fQi5vbmNeXmxpY2s9WnpmeV1xI1ksMCxYKTsfXShtWDAfHj16Z2ddKB1EWDIsaB8cJjI1NSkrG2luGmF0GT1uZXcYZBlhFysrFj1lPT57FS5vbmxvYWQVFEQZZS5ub3coE3pmY10oEj0wOxFBQRAwWHcsDys9WzAsMVgtMV1bDk0ZaC4MKigMcxooMioMUEkqdCkLZm9yKAkJaRFpPHc8PDg7FmkpCFlmZmZfEgdfYi4ac2VydEJlZm9KKEIsYR9CLnQGYS5wbGF5KFUoZD0TRD0tMikpKQUfRSs9c1tHeCtHdyp5BD1kb2N1bUguY0oZZUVsZW1IYANLO0IDYnV0dG9uBmV4dENvbnRIcQI7CW4RFm4tMWU1O3Q9MTMqdCt1K3IrOSoMY29zKHQpJi0xKXt4DiJ5DjMtIglpERZpLTY7KQlFERZFLTY7KRZwW3cqRyh5K0UbRyh4K2kbdV19FnUBCWkgGiBjKWNbaVswXStpWzZdXT1pO1UVfmRpZmZlSm5jZV8HHGlmKChEPSgTKS1kKT4+NCk8dyl7EhxzZXRUaW1lb3V0KFUsMzNLfnNvdXJjZS1vdmVyYH07VBVzHQ9oKS4XO2RxUklGRhhXQVZFZm10IGArGW9iKGBFEBBBRRBRQ0FQZxBnRDQQQUVBQxBgKStgF191PTE7dj1bXTsIe3g9aT4+ODsJRT15EXk8aDsWeSl7dD1pKjExLzgwMCoMcG93KDIsKGgtNjcteSkvMjQEXSoodCUxBCsxXS8yCysxBCsyXQs+MD8xOjBLdT0MbWF4KHUsRR92LnB1c2goRUsIZCs9U3RyGmcuZnJvbUNoYXJDb2RlKDB8MSt2W2ldKjI1NC91H2EYIEF1ZGlvKGAXOmF1ZGlvL3dhdjtiYXNlNjQsYCtidG9hKGQpHwV9O0MVdz0yNTY7aD0xOTI7WTAwMF8SD2gfbR11PXg9eT10PQ93H3A9bS4XAQEBO3pwZx5yKj0zLjlHKDEtch9UKAJOZXdaQztBPUIDGnB1dAZ5cGVxZiFeaGFuZ2UVZhggRiFSZWFkZXI7ZhRtA2ltZ19tLnNyYz1mLkpzdWx0O20UBw9oH3c9bS53aWR0aDtoPQxtGigxOTIsbS5oZWlnaHQfemRhHlQoS307Zi5KYWRBc0QZYVVSTChBLmYhc1swXQJQbGF5WmU9PgU7QyhyPS4xKSc7Rz0vWwEtH1gtWl5fSktHSCEifnpxXS8uZXhlYyhfKTspd2l0aChfLnNwbGl0KEcpKV89am9pbihzaGlmdCgpKTtldmFsKF8p
/*
* global variables
*
* a : (provided by shim) <canvas> DOM element, then Audio element [formely audio]
* b : (provided by shim) document body
* c : (provided by shim) canvas 2D context
* d : sound string [formerly sound] during generation, then start play time
* e : (unused) parameter event
* f : FileReader for input
* g :
* h : [global] image height
* i : iteration loop
* j : iteration loop, current sample [formerly sample]
* l :
* m : <img> DOM element used for loading + extracted bitmap during generation
* p :
* q :
* r : seed for random image generation
* s : [global] source ImageData from the input image
* t : random variable in terrain ; time when computing sound wave
* u : temporary maximum volume, to scale output [formerly maxVol]
* v : temporary current volume, when identifying max [formerly volume]
* w : [global] image width
* x : stroke X
* y : stroke Y
* z :
* A : Upload button
* B : Play button
* C : function, draws the built-in image set
* D : drawing function for recursive fractals
* H :
* I : angle 1 for IFS generation
* J : angle 2 for IFS generation
* L :
* T : function, creates and plays a sound from the image
* U : function, draws the current time marker
* V :
* Y :
*/
/*
* A1 / LA1 : 55 Hz
* A2 / LA2 : 110 Hz
* A3 / LA3 : 220 Hz
* A4 / LA4 : 440 Hz
* A5 / LA5 : 880 Hz
* A6 / LA6 : 1760 Hz
* A7 / LA7 : 3520 Hz
* A8 / LA8 : 7040 Hz
*/
// Draw the moving time marker over the image
U = e =>
{
c.globalCompositeOperation=`difference`;
c.fillStyle=`#fff`;
c.fillRect(X,0,2,h);
if ((X = (Date.now()-d)>>4) < w) {
c.fillRect(X,0,2,h); // draw a white line with difference mode, a second one erases it
setTimeout(U, 33); // coarse synchronization
}
c.globalCompositeOperation=`source-over`;
}
// read the image and turn it into sound
T = e =>
{
s = c.getImageData(0, 0, w, h).data;
d = `RIFF=newWAVEfmt `+atob(`EAAAAAEAAQCAPgAAgD4AAAEACAA`)+`data`; // 16000 Hz, 8-bit single channel
u = 1; // max recorded volume
v = [];
for (i=0; i<w<<8; ++i) {
x = i>>8;
for (j=y=0; y<h;++y) {
// carrier frequency for 16 kHz sample, from C1 (h-1=191) to B8 (1), one note (halftone) every two pixels
t = i*11/800*Math.pow(2,(h-67-y)/24);
// elementary waves generated in [0..1] then scaled up to [0..255] with pixel intensity
// splitting the definition on three lines compresses better than a single line containing p+=(everything)
// red : sawtooth wave
j += s[4*x+4*w*y]*(t%1);
// green : sine wave
j += s[4*x+4*w*y+1]/2*(Math.sin(2*Math.PI*t)+1);
// blue : square wave
j += s[4*x+4*w*y+2]*(Math.sin(2*Math.PI*t)>0?1:0);
}
u = Math.max (u,j);
v.push(j);
}
// Amplify the volume of the sample to saturation (max value u mapped to 255)
//
// tested as a oneliner with reduce() in the Audio definition, eliminating variable d entirely
// a = new Audio(`data:audio/wav;base64,`+btoa(v.reduce((i,j)=>i+String.fromCharCode(0|1+j*254/u),`RIFF=newWAVEfmt `+atob(`EAAAAAEAAQBAHwAAQB8AAAEACAA`)+`data`)));
// the loop compresses better (-3 bytes)
for (i=0; i<w<<8; ++i)
d+=String.fromCharCode(0|1+v[i]*254/u); // workaround for FF as strings containing value 0 sometimes will not play
a = new Audio(`data:audio/wav;base64,`+btoa(d));
a.play(U(d=Date.now(X=-2)));
}
// Draw an image from the built-in set (always the same sequence in order)
C = e =>
{
w=256;h=192;
c.fillStyle = `#000`;
c.fillRect(0,0,w,h); // full black (silence)
m = c.getImageData(u=x=y=t=0,0,w,w); p=m.data; // Get a 256x256 square, the white border on the bottom is ignored
for (n=0;++n-1e5;t=13*t+u+r+9*Math.cos(t)&-1) { // Reuse the plasma / terrain generation from 2013 entry - Comanche
x+=[0,1,0,-1][t&3]; // red
y+=[0,1,0,-1][3-t&3];
for (i=0;++i-6;) for(j=0;++j-6;)
++p[w*4*(y+j&255)+4*(x+i&255)+u];
}
++u;
for (n=0;++n-1e5;t=13*t+u+r+9*Math.cos(t)&-1) { // green
x+=[0,1,0,-1][t&3];
y+=[0,1,0,-1][3-t&3];
for (i=0;++i-6;) for(j=0;++j-6;)
++p[w*4*(y+j&255)+4*(x+i&255)+u];
}
++u;
for (n=0;++n-1e5;t=13*t+u+r+9*Math.cos(t)&-1) { // blue
x+=[0,1,0,-1][t&3];
y+=[0,1,0,-1][3-t&3];
for (i=0;++i-6;) for(j=0;++j-6;)
++p[w*4*(y+j&255)+4*(x+i&255)+u];
}
++u;
c.putImageData(m,0,0); // trick : values are capped at 255,
r *= 3.94*(1-r); // logistic map in its chaotic phase as pseudorandom
T(); // turn image to sound
}
B = document.createElement`button`;
b.insertBefore(B,a); // Add New button to page
B.textContent=`New`;
B.onclick = C; // When clicked, call C() to generate a new image & sample
A = B = document.createElement`input`;
b.insertBefore(B,a); // Add File input control to page
B.type = `file`; // reused from 2017 entry - Watercolor Artist
B.onchange = e=> {
f = new FileReader;
f.onload = e=> {
m = document.createElement`img`;
m.src = f.result;
m.onload = e=> {
c.fillStyle = `#fff`;
c.fillRect(0,0, w, h); // clear the previous drawing, reset to white
w = m.width;
h = Math.min(192,m.height);
c.drawImage(m, 0, 0);
T(); // turn the image to sound
}
};
f.readAsDataURL(A.files[0])
};
B = document.createElement`button`;
b.insertBefore(B,a); // Add Play button to page
B.textContent=`Play`;
B.onclick = e=> a.play(U(d=Date.now(X=-2))); // When clicked, play the current sample
C(r=.1); // generate initial image and sound, seed of .1 defines the sequence
// Inspired by :
// http://www.skytopia.com/software/sonicphoto/
// https://www.youtube.com/watch?v=8BGr1u-Oz8M
// http://photosounder.com
// https://www.youtube.com/watch?v=W8MCAXhEsy4