const balls=[],radius=20,num=64,maxSpeed=15;
Shader=getShader(this._renderer);
createCanvas(windowWidth, windowHeight, WEBGL);
for (let i=0;i<num;i++) {
createFileInput(handleFile).position(0,0).style('-webkit-appearance: none;').style('background',color(0,0,0,200)).style('color', color(255));
if (mouseIsPressed) {pinned.vx=0;pinned.vy=0;pinned.x=mouseX;pinned.y=mouseY;}
let dstSq=(b.x-bb.x)**2+(b.y-bb.y)**2;
if (dstSq<((b.rad+bb.rad)/2)**2) {dstSq=((b.rad+bb.rad)/2)**2;}
let f=G*b.rad*bb.rad/dstSq;
let a=atan2(bb.y-b.y,bb.x-b.x);
b.vx=constrain(b.vx+cos(a)*f/b.rad,-maxSpeed,maxSpeed);
b.vy=constrain(b.vy+sin(a)*f/b.rad,-maxSpeed,maxSpeed);
let a=atan2(mouseY-b.y,mouseX-b.x);
b.vx=constrain(b.vx+cos(a)*f/b.rad,-maxSpeed,maxSpeed);
b.vy=constrain(b.vy+sin(a)*f/b.rad,-maxSpeed,maxSpeed);
if (b.x< b.rad) {b.x= b.rad;b.vx*=-1;}
if (b.x>width -b.rad) {b.x=width -b.rad;b.vx*=-1;}
if (b.y< b.rad) {b.y= b.rad;b.vy*=-1;}
if (b.y>height-b.rad) {b.y=height-b.rad;b.vy*=-1;}
data.push(b.x,b.y,freq[i]/5);
Shader.setUniform("balls",data);
function mousePressed() {
let dst=(pinned.x-mouseX)**2+(pinned.y-mouseY)**2;
for (let i=1;i<balls.length;i++) {
let d=(balls[i].x-mouseX)**2+(balls[i].y-mouseY)**2;
if (d<dst) {dst=d;pinned=balls[i];}
function handleFile(file) {
sound=loadSound(file.data,()=>{sound.play()});
function getShader(_renderer) {
attribute vec3 aPosition;
attribute vec2 aTexCoord;
vec4 positionVec4=vec4(aPosition,1.);
positionVec4.xy=positionVec4.xy*2.-1.;
gl_Position = positionVec4;
const float WIDTH=${windowWidth}.;
const float HEIGHT=${windowHeight}.;
uniform vec3 balls[${num}];
float x=vTexCoord.x*WIDTH;
float y=HEIGHT-vTexCoord.y*HEIGHT;
for (int i=0;i<${num};i++) {
r+=b.z*b.z/((b.x-x)*(b.x-x)+(b.y-y)*(b.y-y));
gl_FragColor = vec4(r,g,b,1.);
return new p5.Shader(_renderer, vert, frag);