Perlin Noise and JavaScript

After looking around HTML5 demos, I got interested in the “Perlin Noise”, which provides a natural fluctuation of randomness. This small interest resulted in a week-long struggle… Finally I could make a working example, I’m putting here (and will be focusing on another topic for a while).

Regarding the perline noise generation in JavaScript, the following code was nice and handy.
https://gist.github.com/banksean/304522

Major struggle for me was that I couldn’t find good animation or graphical filter library in JavaScript, though I could find many good ActionScript ones for flash. For example, the following sites have nice one, but I couldn’t find an easy way to reproduce them in JavaScript (I can see some shape or image(sprite) anmation libraries like create.js, enchant.js, etc, though).

The following is a working example and some codes.

screenshot
perline_color
Working Example
http://jsdo.it/parroty/4FMA

Source Code – draw.js

var canvas;
var canvasWidth;
var canvasHeight;
var context;

var SPEED      = 5;
var MATRIX_NUM = 3;

var rectWidth  = 465;
var rectHeight = 465;
var mapSize    = 4;
var unitSize   = 2;

var matrixes;
var imageData;

var count = 0;

function init() {
  canvas = document.getElementById('c');
  document.addEventListener('resize', resize);
  resize();

  initPoints();
  imageData = context.createImageData(rectWidth, rectHeight);
  setInterval(loop, 1000 / 30);
}

function resize() {
  canvasWidth  = canvas.width = window.innerWidth;
  canvasHeight = canvas.height = window.innerHeight;
  context      = canvas.getContext('2d');
}

function initPoints() {
  matrixes = [];
  for(var i = 0; i < MATRIX_NUM; i++) {
    var matrix = new Matrix(rectWidth * mapSize, rectHeight * mapSize);
    var simplexNoise = new SimplexNoise;

    matrix.updatePoints(function(x, y) {
      return Math.floor(simplexNoise.noise(x / 150, y / 150)*128) + 128;
    });

    matrix.setRandomSpeed(SPEED)
    matrix.setUnit(unitSize, unitSize);

    matrixes.push(matrix);
  }
}

function loop() {
  for(var i = 0; i < MATRIX_NUM; i++) {
    matrixes[i].movePoints();
  }

  var data = imageData.data;
  for(var x = 0; x < rectWidth; x++) {
    for(var y = 0; y < rectHeight; y++) {
      var index = (x * rectHeight + y) * 4;

      data[index + 0] = matrixes[0].get(x, y);  // red
      data[index + 1] = matrixes[1].get(x, y);  // green
      data[index + 2] = matrixes[2].get(x, y);  // blue
      data[index + 3] = 100;                    // alpha
    }
  }
  context.putImageData(imageData, 0, 0);

  count += 1;
}

// Init
window.addEventListener ? window.addEventListener('load', init, false) : window.load = init;

Source Code – lib.js

var Point = function(x, y) {
  this.x = x;
  this.y = y;
}

var Matrix = function(width, height) {
  this.width  = width;
  this.height = height;

  this.count  = new Point(0, 0);
  this.speed  = new Point(0, 0);
  this.sign   = new Point(0, 0);
  this.unit   = new Point(1, 1);
  this.offset = new Point(Math.floor(width / 2), Math.floor(height / 2));

  this.points = [];
  for(var i = 0; i < width; i++) {
    this.points.push(new Array(height));
  };
  this.updatePoints(function(x, y){
    return 0;
  });
}

Matrix.prototype = {
  get: function(x, y) {
    var ix = this.getIndex(x + this.offset.x, this.width);
    var iy = this.getIndex(y + this.offset.y, this.height);

    return this.points[ix][iy];
  },

  set: function(x, y, value) {
    var ix = this.getIndex(x, this.width);
    var iy = this.getIndex(y, this.height);

    this.points[ix][iy] = value;
  },

  setSpeed: function(dx, dy) {
    this.speed.x = Math.abs(dx);
    this.speed.y = Math.abs(dy);

    this.sign.x = dx / Math.abs(dx);
    this.sign.y = dy / Math.abs(dy);
  },

  setRandomSpeed: function(max) {
    var rad = (360 * Math.random()) * (Math.PI / 180);
    var sx = Math.sin(rad);
    var sy = Math.cos(rad);

    this.setSpeed(sx, sy);
  },

  setUnit: function(x, y) {
    this.unit.x = x;
    this.unit.y = y;
  },

  getIndex: function(value, border) {
    var offset = Math.abs(value) % (2 * border);
    if(offset >= border) {
      return (2 * border) - offset - 1;
    }
    else {
      return offset;
    }
  },

  updatePoints: function(func) {
    var p = [];
    for(var i = 0; i < this.width; i++) {
      for(var j = 0; j < this.height; j++) {
        p[i * this.height + j] = func.call(null, i, j);
      }
    }

    for(var i = 0; i < this.width; i++) {
      for(var j = 0; j < this.height; j++) {
        this.set(i, j, p[i * this.height + j]);
      }
    }
  },

  eachPoint: function(func) {
    for(var i = 0; i < this.width; i++) {
      for(var j = 0; j < this.height; j++) {
        func.call(null, i, j, this.get(i, j));
      }
    }
  },

  movePoints: function() {
    this.count.x += this.speed.x;
    this.count.y += this.speed.y;

    if(this.count.x >= 1) {
       this.offset.x -= this.sign.x * this.unit.x;
       this.count.x--;
     }

    if(this.count.y >= 1) {
      this.offset.y -= this.sign.y * this.unit.y;
      this.count.y--;
    }
  },

  drawPoints: function() {
    for(var i = 0; i < this.width; i++) {
      var a = []
      for(var j = 0; j < this.height; j++) {
        a.push(("0" + this.get(i, j)).slice(-2));
      }
      console.log(a.join(","));
    }
    console.log("");
  },
}
Advertisements

Posted on April 25, 2013, in JavaScript. Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: