Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
perlin.js
=========
# perlin.js

#### A JavaScript Perlin Noise Generator
## A JavaScript Perlin Noise Generator

This short js library allows you to easily incorporate <a href='https://en.wikipedia.org/wiki/Perlin_noise'>perlin noise</a> into your projects.
This short JS library allows you to easily incorporate <a href='https://en.wikipedia.org/wiki/Perlin_noise'>perlin noise</a> into your projects with deterministic seeds.

---

### Python

Note that I have also translated the js code into Python, but I have not written any documentation as this library will mainly be used in js.
Note that the python here is outdated and does not include deterministic seeds.

---

Expand All @@ -18,36 +17,37 @@ Note that I have also translated the js code into Python, but I have not written
Simply include the source in your application's HTML, no downloading required:

```html
<script src='http://joeiddon.github.io/perlin/perlin.js'></script>
```

or you can use the, slightly shorter, `git.io` redirect:

```html
<script src='http://git.io/perlin.js'></script>
<script src='https://github.com/Xynerian/seeded-perlin.git'></script>
```

---

### Usage

There are 2 main functions that are provided: `perlin.seed` and `perlin.get`.

The `perlin.get` function has the format:

There are 2 main functions that are provided: `perlin.clearMemory` and `perlin.get`.
And a property used to determine the noise generated `perlin.seed`.
### perlin.seed
Format:
```javascript
perlin.seed
```
The seed is used to determine what noise pattern is generated.
### perlin.get
Format:
```javascript
perlin.get(x, y)
```
where `x` and `y` are floating point numbers.

The function will return a float in the range `-1.0` to `1.0` representing the 'noise-intensity' at that point. As for the scale, the coordinate system is setup as a grid with vertexes at integer coordinates. These vertexes are the peaks and troughs of the noise. All floating point coordinates between inside cells will give smoothly interpolated values between.

All that `perlin.seed()` does (to be called with no arguments), is reset the stored noise so that you can generate fresh noise. On each reload of the library, this will be reset anyway, and if you were to just offset all your `perlin.get` calls away from your previous calls, you would achieve the same effect of generating new noise. However, by doing this, you are of course sacrificing some memory as the previous noise remains saved (this is negligible, but is something to bare in mind).
### perlin.clearMemory
All that `perlin.clearMemory()` does (to be called with no arguments), is reset the stored noise so that you can generate fresh noise. On each reload of the library, this will be reset anyway, and if you were to just offset all your `perlin.get` calls away from your previous calls, you would achieve the same effect of generating new noise. However, by doing this, you are of course sacrificing some memory as the previous noise remains saved (this is negligible, but is something to bare in mind).

---

### Examples

I created a basic example of the noise being generated which you can see its source in this GitHub repository. The demo creates the most basic display of noise, but I added a heatmap-style effect to it (using the hsl() colour format) which makes the result clearer. You can view it in action <a href='http://joeiddon.github.io/perlin/demo'>here</a>.
I created a basic example of the noise being generated which you can see its source in this GitHub repository. The demo creates the most basic display of noise, but I added a heatmap-style effect to it (using the hsl() colour format) which makes the result clearer.

Obviously that demo is the most basic use of the library. Feel free to browse <a href='http://joeiddon.github.io'>my website</a> to see some other uses.
Obviously that demo is the most basic use of the library. I don't yet have an online demo available but if you can pretty easily copy-paste it into an HTML file and then open that file in your web browser to see it.
143 changes: 118 additions & 25 deletions demo.html
Original file line number Diff line number Diff line change
@@ -1,34 +1,127 @@
<!DOCTYPE html>
<html>

<head>
<script src='perlin.js'></script>
</head>

<body>
<canvas id='cnvs'></canvas>
<script>
'use strict';
let cnvs = document.getElementById('cnvs');
cnvs.width = cnvs.height = 512;
let ctx = cnvs.getContext('2d');
<canvas id='cnvs'></canvas>
<input type="text" id="seed" placeholder="Seed">
<button onclick="draw()">Generate</button>
<script> //Perlin Script
'use strict';
let perlin = {
seed: Math.random() + Math.random() * 10,
gradients: {},
memory: {},
seedify: function (str) {
let h = 2166136261;
for (let i = 0; i < str.length; i++) {
h ^= str.charCodeAt(i);
h = Math.imul(h, 16777619);
}
return h >>> 0;
},
rand: function () {
if (typeof this.seed === "string") {
this.seed = this.seedify(this.seed);
}
this.seed = (this.seed + 0x6D2B79F5) >>> 0;
let t = this.seed;
let r = Math.imul(t ^ (t >>> 15), t | 1);
r ^= r + Math.imul(r ^ (r >>> 7), r | 61);
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
},
rand_vect: function () {
const theta = this.rand() * Math.PI * 2;
return {
x: Math.cos(theta),
y: Math.sin(theta)
}
},
dot_prod_grid: function (x, y, vx, vy) {
let g_vect;
let d_vect = { x: x - vx, y: y - vy };
if (this.gradients[[vx, vy]]) {
g_vect = this.gradients[[vx, vy]];
} else {
g_vect = this.rand_vect();
this.gradients[[vx, vy]] = g_vect;
}
return d_vect.x * g_vect.x + d_vect.y * g_vect.y;
},
smootherstep: function (x) {
return 6 * x ** 5 - 15 * x ** 4 + 10 * x ** 3;
},
interp: function (x, a, b) {
return a + this.smootherstep(x) * (b - a);
},
clearMemory: function () {
this.gradients = {};
this.memory = {};
},
get: function (x, y) {
if (this.memory.hasOwnProperty([x, y]))
return this.memory[[x, y]];
let xf = Math.floor(x);
let yf = Math.floor(y);
//interpolate
let tl = this.dot_prod_grid(x, y, xf, yf);
let tr = this.dot_prod_grid(x, y, xf + 1, yf);
let bl = this.dot_prod_grid(x, y, xf, yf + 1);
let br = this.dot_prod_grid(x, y, xf + 1, yf + 1);
let xt = this.interp(x - xf, tl, tr);
let xb = this.interp(x - xf, bl, br);
let v = this.interp(y - yf, xt, xb);
this.memory[[x, y]] = v;
return v;
}
}
perlin.clearMemory();
</script>
<script> //Demo Script
'use strict';
let cnvs = document.getElementById('cnvs');
cnvs.width = cnvs.height = 512;
let ctx = cnvs.getContext('2d');

const GRID_SIZE = 4;
const RESOLUTION = 128;
const COLOR_SCALE = 250;
const GRID_SIZE = 4;
const RESOLUTION = 128;
const COLOR_SCALE = 250;

let pixel_size = cnvs.width / RESOLUTION;
let num_pixels = GRID_SIZE / RESOLUTION;
let pixel_size = cnvs.width / RESOLUTION;
let num_pixels = GRID_SIZE / RESOLUTION;

for (let y = 0; y < GRID_SIZE; y += num_pixels / GRID_SIZE){
for (let x = 0; x < GRID_SIZE; x += num_pixels / GRID_SIZE){
let v = parseInt(perlin.get(x, y) * COLOR_SCALE);
ctx.fillStyle = 'hsl('+v+',50%,50%)';
ctx.fillRect(
x / GRID_SIZE * cnvs.width,
y / GRID_SIZE * cnvs.width,
pixel_size,
pixel_size
);
}
}
</script>
for (let y = 0; y < GRID_SIZE; y += num_pixels / GRID_SIZE) {
for (let x = 0; x < GRID_SIZE; x += num_pixels / GRID_SIZE) {
let v = parseInt(perlin.get(x, y) * COLOR_SCALE);
ctx.fillStyle = 'hsl(' + v + ',50%,50%)';
ctx.fillRect(
x / GRID_SIZE * cnvs.width,
y / GRID_SIZE * cnvs.width,
pixel_size,
pixel_size
);
}
}

function draw() {
perlin.seed = document.getElementById('seed').value;
perlin.clearMemory();
for (let y = 0; y < GRID_SIZE; y += num_pixels / GRID_SIZE) {
for (let x = 0; x < GRID_SIZE; x += num_pixels / GRID_SIZE) {
let v = parseInt(perlin.get(x, y) * COLOR_SCALE);
ctx.fillStyle = 'hsl(' + v + ',50%,50%)';
ctx.fillRect(
x / GRID_SIZE * cnvs.width,
y / GRID_SIZE * cnvs.width,
pixel_size,
pixel_size
);
}
}
}
</script>
</body>

</html>
72 changes: 48 additions & 24 deletions perlin.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,69 @@
'use strict';
let perlin = {
rand_vect: function(){
let theta = Math.random() * 2 * Math.PI;
return {x: Math.cos(theta), y: Math.sin(theta)};
seed: Math.random() + Math.random() * 10,
gradients: {},
memory: {},
seedify: function (str) {
let h = 2166136261;
for (let i = 0; i < str.length; i++) {
h ^= str.charCodeAt(i);
h = Math.imul(h, 16777619);
}
return h >>> 0;
},
rand: function () {
if (typeof this.seed === "string") {
this.seed = this.seedify(this.seed);
}
this.seed = (this.seed + 0x6D2B79F5) >>> 0;
let t = this.seed;
let r = Math.imul(t ^ (t >>> 15), t | 1);
r ^= r + Math.imul(r ^ (r >>> 7), r | 61);
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
},
rand_vect: function () {
const theta = this.rand() * Math.PI * 2;
return {
x: Math.cos(theta),
y: Math.sin(theta)
}
},
dot_prod_grid: function(x, y, vx, vy){
dot_prod_grid: function (x, y, vx, vy) {
let g_vect;
let d_vect = {x: x - vx, y: y - vy};
if (this.gradients[[vx,vy]]){
g_vect = this.gradients[[vx,vy]];
let d_vect = { x: x - vx, y: y - vy };
if (this.gradients[[vx, vy]]) {
g_vect = this.gradients[[vx, vy]];
} else {
g_vect = this.rand_vect();
this.gradients[[vx, vy]] = g_vect;
}
return d_vect.x * g_vect.x + d_vect.y * g_vect.y;
},
smootherstep: function(x){
return 6*x**5 - 15*x**4 + 10*x**3;
smootherstep: function (x) {
return 6 * x ** 5 - 15 * x ** 4 + 10 * x ** 3;
},
interp: function(x, a, b){
return a + this.smootherstep(x) * (b-a);
interp: function (x, a, b) {
return a + this.smootherstep(x) * (b - a);
},
seed: function(){
clearMemory: function () {
this.gradients = {};
this.memory = {};
},
get: function(x, y) {
if (this.memory.hasOwnProperty([x,y]))
return this.memory[[x,y]];
get: function (x, y) {
if (this.memory.hasOwnProperty([x, y]))
return this.memory[[x, y]];
let xf = Math.floor(x);
let yf = Math.floor(y);
//interpolate
let tl = this.dot_prod_grid(x, y, xf, yf);
let tr = this.dot_prod_grid(x, y, xf+1, yf);
let bl = this.dot_prod_grid(x, y, xf, yf+1);
let br = this.dot_prod_grid(x, y, xf+1, yf+1);
let xt = this.interp(x-xf, tl, tr);
let xb = this.interp(x-xf, bl, br);
let v = this.interp(y-yf, xt, xb);
this.memory[[x,y]] = v;
let tl = this.dot_prod_grid(x, y, xf, yf);
let tr = this.dot_prod_grid(x, y, xf + 1, yf);
let bl = this.dot_prod_grid(x, y, xf, yf + 1);
let br = this.dot_prod_grid(x, y, xf + 1, yf + 1);
let xt = this.interp(x - xf, tl, tr);
let xb = this.interp(x - xf, bl, br);
let v = this.interp(y - yf, xt, xb);
this.memory[[x, y]] = v;
return v;
}
}
perlin.seed();
perlin.clearMemory();