I created this basic metaball visualization in JavaScript. While it does look nice, it is very slow if you increase number of balls and/or make drawing area larger. I think that the performance is so bad because it iterates through every pixel every time the frame is updated but I cannot think anyway how I could update only those pixels where something has actually happened.
Here's the code:
// canvas
var c = document.getElementById("canvas");
width = c.width = /*window.innerWidth;*/1280;
height = c.height = /*window.innerHeight;*/720;
var ctx = c.getContext("2d");
// imagedata
var imageData = ctx.createImageData(width,height);
var data = imageData.data;
// array for balls
var balls = [];
var num = 20;
// random number generation
function random(min, max){
var num = Math.floor(Math.random()*(max-min+1)+min);
return num;
}
// metaball
function ball() {
this.posX = random(0, width);
this.posY = random(0, height);
this.dx = random(-4,4);
this.dy = random(-4,4);
this.radius = random(10, 100);
}
// ball movement
ball.prototype.move = function() {
if (this.posX > width || this.posX < 0) { // change direction if edge is encountered, x-axis
this.dx = -(this.dx);
}
if (this.posY > height || this.posY < 0 ) { // change direction if edge is encountered, y-axis
this.dy = -(this.dy);
}
this.posX += this.dx; // move balls, x-axis
this.posY += this.dy; // move balls, y-axis
}
// create balls and put them into an array
for (i=0; i<num;i++) {
var metaball = new ball();
balls.push(metaball);
}
// draw and move metaballs
function Loop(){
for (var y=0; y<height; y++) { // loop through draw area pixels, height
for (var x=0; x<width; x++) { // loop through draw area pixels, width
var index = (x+y*width)*4; // pixel index
var color = 0; // pixel color
for (i=0; i<balls.length; i++) {
color += balls[i].radius * 10000 / (Math.pow((x - balls[i].posX),2) + Math.pow((y - balls[i].posY),2)); // metaball calculation formula
}
// assign color to pixel based on previous formula
data[index+0] = color*1.05; // r
data[index+1] = color; // g
data[index+2] = color*1.4; // b
data[index+3] = 255; // a
}
}
// move balls
for (i=0; i<balls.length; i++) {
balls[i].move();
}
// put imagedata on canvas
ctx.putImageData(imageData, 0,0);
// animate
requestAnimationFrame(Loop);
}
// call loop function
Loop();
<!DOCTYPE html>
<html>
<head>
<title>metaballs</title>
<style type="text/css">
body {
padding: 0px;
margin: 0px;
}
</style>
</head>
<body>
<div>
<canvas id="canvas">
</canvas>
</div>
<script type="text/javascript" src="metaballs.js">
</script>
</body>
</html>
Is there anything I can do to optimize this?
Since I'm a total beginner in JavaScript and programming in general, I would be very happy to hear if there are better practices than what I've used here :)