Public
Edited
Apr 22, 2024
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
W = width
Insert cell
H = W*9/16
Insert cell
cvs1 ={
// first I am getting a Canvas rendering context the `observablehq` way and fill it with black.
// W and H are the width and height of the canvas.
const ctx = DOM.context2d(W, H);
ctx.fillStyle = "#000";
ctx.fillRect(0,0,W,H);

// Here I create an array of 300 points randomly placed on the canvas.
const points = Array.from({length:300}, _=> [Math.random()*W, Math.random()*H]);
ctx.globalAlpha = 0.4;
let frame = 0;

// Now I am calling requestAnimationFrame in order to start a browser animation loop, at the same time saving the animation frame id in the `af` variable.
let af = requestAnimationFrame(function tick() {

// during every animation frame, I loop through the points array and if they still are on the canvas ... (see below)
points.forEach(([x,y],i) => {



// I take the noise value at the same position. Keeping in mind that Worley noise is defined as the distance to the n'th closest seed point, the value of `d` can be anywhere between 0 and canvas width
const d = worley([x,y]);
// I set the draw color from the palette.
ctx.strokeStyle = palette[i%palette.length];
// begin a new line
ctx.beginPath();
// make it start at current point position `[x,y]`
ctx.moveTo(x,y);



// then I'm advancing the current point position by adding the vector obtained by interpreting d (which can be any size as said before) as the angle of motion. The `cossin` function is defined as `cossin = (a, n) => [Math.cos(a)*n, Math.sin(a)*n]`
// in order to advance every point with some different amount I call the cossin function with the second parameter calculated as 1 plus index of the point modulo 10 having the value between (1 and 10). This approach ensures every traveling point will allways travel the same distance every step.
points[i] = add([x,y], cossin(d, (1+i%10)));

if (d<ditch || d>ditch+20){
// having computed and stored the next position of current point, I'm calling lineTo canvas function to draw a line to the new point position.
ctx.lineTo(...points[i]);
ctx.stroke();
}
});


// Here I am drawing as red circles the seed points I have used to define the Worley noise. This is just for illustrantion purposes in order for the reader to see te relation with the final rendering.
ctx.fillStyle = "red";
seeds.forEach(([x,y])=> {
ctx.beginPath();
ctx.arc(x,y, 3, 0, Math.PI*2);
ctx.fill();
});
// here I'm letting the browser know I want to draw one more frame. Of course, if I reached the maximum number of frames I want to draw, I can skip requesting a new frame
(frame++<2000) && (af = requestAnimationFrame(tick));
});



// this part is necesary on observablehq platform in order to avoid running invisible frames in background. Basically it said that if the current cell was run again, just stop the previously running animation.
invalidation.then(_=> cancelAnimationFrame(af));

// this is also a requirement of the platform. In order to render something the block needs to return a DOM element.
return ctx.canvas;
}
Insert cell
cvs ={
const ctx = DOM.context2d(W, H);
ctx.fillStyle = "#ccc";
ctx.fillRect(0,0,W,H);

for(let x = 0; x<W;x++)
for(let y = 0; y<H;y++) {
const w = worley([x,y])/5;
ctx.fillStyle = `hsl(30deg 0% ${w}%)`;
ctx.fillRect(x,y,1,1);
}

ctx.fillStyle = "red";
seeds.forEach(([x,y])=> {
ctx.beginPath();
ctx.arc(x,y, 3, 0, Math.PI*2);
ctx.fill();
});
return ctx.canvas;
}
Insert cell
Insert cell
/// these points will be used to define the worley noise

seeds = Array.from({length:seedCount}, _ => [50+Math.random()*(W-100), 50+Math.random()*(H-100)]);
Insert cell
euclidean = ([ax,ay], [bx, by]) => Math.hypot(ax-bx, ay-by)
Insert cell
euclidean([0,0], [10,10])
Insert cell
Insert cell
taxi = ([ax,ay], [bx, by]) => Math.abs(ax-bx) +Math.abs(ay-by)
Insert cell
taxi([0,0], [10,10])
Insert cell
Insert cell
blend = t => (a,b) => t*euclidean(a,b) + (1-t)*taxi(a,b);
Insert cell
Insert cell
distance = [euclidean, taxi, blend(t)][distIdx]
Insert cell
distance([0,0], [10,10])
Insert cell
Insert cell
import {cossin, add} from "@max-generis/vector-math"
Insert cell
worley = (p) => {
const distances = seeds.map(s => distance(s, p))
.sort((a,b)=>a-b);
return distances[level]
}
Insert cell
import {Palettes} from "@max-generis/palettes"

Insert cell
palette = Palettes.Colorful;
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more