Skip to main content
edited tags
Link
200_success
  • 145.7k
  • 22
  • 191
  • 481
Source Link

JavaScript metaballs optimization

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 :)