Inspired by this [question][1], I modified the script to run a simple simulation of [Taylor-Green vortex][2] using [LBM][3] and post-processes it using [P5*js][4] (an official Javascript port of the [Processing][5] API). Unfortunately, it is not performing well, but as I am new to Javascript programming, I lack the insight to really optimize the code. Getting some advice and suggestions for improvement would be greatly appreciated. A working codepen version can be found [here][6]. // 2D vector class function vec2(x,y){ this.x = x; this.y = y; } vec2.prototype = { scale: function(s){ return new vec2(this.x*s,this.y*s); }, add: function(v){ return new vec2(this.x+v.x,this.y+v.y); }, subtract: function(v){ return new vec2(this.x-v.x,this.y-v.y); }, dot: function(v){ return this.x*v.x+this.y+v.y; }, length: function(){ return Math.sqrt(this.x*this.x+this.y*this.y); }, normalise: function(){ var l = 1/this.length(); return new vec2(this.x*l, this.y*l); }, }; // domain class function domain(nx, ny) { this.nx = nx; // domain width this.ny = ny; // domain height this.f = []; // distribution array this.ftmp = []; // temporary array this.dens = []; // density array this.vel = []; // velocity array this.omega = 1; // relaxation frequency this.e = [ // discrete velocity set new vec2(0,0), new vec2(0,1), new vec2(1,0), new vec2(0,-1), new vec2(-1,0), new vec2(1,1), new vec2(1,-1), new vec2(-1,-1), new vec2(-1,1) ]; this.w = [ // weights 4/9, 1/9, 1/9, 1/9, 1/9, 1/36, 1/36, 1/36, 1/36 ]; // Arrays initialization for (var x=0; x<this.nx; x++) { this.f[x] = []; this.ftmp[x] = []; this.dens[x] = []; this.vel[x] = []; for (var y=0; y<this.ny; y++) { this.f[x][y] = []; this.ftmp[x][y] = []; } } } domain.prototype = { init: function(){ // Initializes Taylor-Green vortex var kx = 2*Math.PI/this.nx; var ky = 2*Math.PI/this.ny; var kxkx = kx*kx; var kyky = ky*ky; var ksq = kxkx + kyky; var k = Math.sqrt(ksq); var dens0 = 1; this.umax = 0.1; var u0 = 4*this.umax; this.densmax = dens0 + 3*dens0*u0*u0/4; for (var x=0; x<this.nx; x++){ for (var y=0; y<this.ny; y++){ var u = u0*ky/k*Math.cos(kx*x)*Math.sin(ky*y); var v = -u0*kx/k*Math.sin(kx*x)*Math.cos(ky*y); this.dens[x][y] = dens0 + 3*dens0*u0*u0/4*(kyky/ksq*Math.cos(2*kx*x)+kxkx/ksq*Math.sin(2*ky*y)); this.vel[x][y] = new vec2(u,v); for(var i=0; i<9; i++){ // Initialize using equilibrium distribution var uu = this.vel[x][y].x*this.vel[x][y].x + this.vel[x][y].y*this.vel[x][y].y; var eu = this.e[i].x*this.vel[x][y].x + this.e[i].y*this.vel[x][y].y; this.f[x][y][i] = this.w[i]*this.dens[x][y]*(1+3*eu+4.5*eu*eu-1.5*uu); } } } }, collide: function(){ for(var x=1; x<this.nx-1; x++){ for(var y=1; y<this.ny-1; y++){ // calculate density var rho = 0; for(var i=0; i<9; i++){ rho += this.f[x][y][i]; } this.dens[x][y] = rho; // calculate velocity var u = new vec2(0,0); for(var i=1; i<9; i++){ u = u.add( this.e[i].scale( this.f[x][y][i] ) ); } u = u.scale( 1/rho ); this.vel[x][y] = u; // Perform collision step and save to temp array ftmp var uu = u.x*u.x + u.y*u.y; for(var i=0; i<9; i++){ var eu = u.x*this.e[i].x + u.y*this.e[i].y; var fiEq = this.w[i]*rho*(1+3*eu+4.5*eu*eu-1.5*uu); var fiCol = -this.omega*(this.f[x][y][i]-fiEq); // bgk this.ftmp[x][y][i] = this.f[x][y][i] + fiCol; } } } }, periodic: function(){ // Apply periodic boundary conditions on ftmp // x-periodic for(var y=1; y<this.ny-1; y++){ for(var i=0; i<9; i++){ this.ftmp[0][y][i] = this.ftmp[this.nx-2][y][i]; this.ftmp[this.nx-1][y][i] = this.ftmp[1][y][i]; } } // y-periodic for(var x=1; x<this.nx-1; x++){ for(var i=0; i<9; i++){ this.ftmp[x][0][i] = this.ftmp[x][this.ny-2][i]; this.ftmp[x][this.ny-1][i] = this.ftmp[x][1][i]; } } // corner treatment for(var i=0; i<9; i++){ this.ftmp[0][0][i] = this.ftmp[this.nx-2][this.ny-2][i]; this.ftmp[this.nx-1][this.ny-1][i] = this.ftmp[1][1][i]; this.ftmp[this.nx-1][0][i] = this.ftmp[1][this.ny-2][i]; this.ftmp[0][this.ny-1][i] = this.ftmp[this.nx-2][1][i]; } }, stream: function(){ // Perform streaming step ftmp -> f for(var x=1; x<this.nx-1; x++){ for(var y=1; y<this.ny-1; y++){ for(var i=0; i<9; i++){ this.f[x][y][i] = this.ftmp[x-this.e[i].x][y-this.e[i].y][i]; } } } } } function simulation(){ var sim = function(p) { var nx = 200, ny = 200; var myDomain = new domain(nx, ny); p.setup = function() { p.createCanvas(nx, ny) .parent('sim'); //p.frameRate(30); myDomain.init(); p.noStroke(); p.colorMode(p.RGB, 1); } p.draw = function() { myDomain.collide(); myDomain.periodic(); myDomain.stream(); var dens = myDomain.dens; var vel = myDomain.vel; var densmax = myDomain.densmax; var umax = myDomain.umax; for (var x = 0; x < p.width; x++) { for (var y = 0; y < p.height; y++ ) { //var v = dens[x][y]/densmax; var v = vel[x][y].length()/umax; p.stroke(v, 0, 1-v); p.point(x, y); } } } } return new p5(sim); } $( document ).ready(simulation()); *Edit 1:* Running the profiler in Chrome results in: [![enter image description here][7]][7] This shows that most of the CPU is being used by P5*JS rather than the code. [1]: https://physics.stackexchange.com/questions/208924/problems-implementing-lattice-boltzmann-method [2]: https://en.wikipedia.org/wiki/Taylor%E2%80%93Green_vortex [3]: https://en.wikipedia.org/wiki/Lattice_Boltzmann_methods [4]: http://p5js.org/ [5]: https://processing.org/ [6]: http://codepen.io/nlooije/pen/MaoEMJ [7]: https://i.sstatic.net/bkCQd.png