Public
Edited
Aug 22, 2023
Importers
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
interactionCanvas = {
const noise = noise2(params);
const img = perlinImage(noise);
const { width, height } = img;
const canvas = DOM.canvas(width, height);
const ctx = canvas.getContext("2d");
let circle = mutable sampleCircle;
const refresh = () => {
ctx.clearRect(0, 0, width, height);
ctx.drawImage(img, 0, 0);
ctx.beginPath();
const n = 500;
const [x0, y0] = circle.center;
const r = circle.radius;
mutable sampleCircle = circle;
for (let i = 0; i < n; i++) {
const ang = (i / n) * Math.PI * 2;
const [cos, sin] = [Math.cos(ang), Math.sin(ang)];
const R = (noise(cos * r + x0, sin * r + y0) + 1) * r;
ctx.lineTo(x0 + cos * R, y0 + sin * R);
}
ctx.closePath();
ctx.lineWidth = 2;
ctx.strokeStyle = "orange";
ctx.stroke();
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
circle.draw(ctx);
};
refresh();
let mouse;
canvas.onmousedown = (e) => {
refresh();
mouse = [e.offsetX, e.offsetY];
if (vec2.dist(mouse, circle.center) < 4) {
canvas.onmousemove = (e) => {
let newmouse = [e.offsetX, e.offsetY];
circle.center = vec2.add(
[],
circle.center,
vec2.sub([], newmouse, mouse)
);
mouse = newmouse;
refresh();
};
} else if (vec2.dist(mouse, circle.borderPoint) < 4) {
canvas.onmousemove = (e) => {
let newmouse = [e.offsetX, e.offsetY];
circle.borderPoint = vec2.add(
[],
circle.borderPoint,
vec2.sub([], newmouse, mouse)
);
mouse = newmouse;
refresh();
};
}
};
canvas.onmouseup = () => {
canvas.onmousemove = null;
};
return canvas;
}
Insert cell
data
X
x
Y
y
Color
Size
Facet X
Facet Y
Mark
Auto
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
mutable noiseloop = new NoiseLoop(
sampleCircle.center,
sampleCircle.radius,
noise2(params),
{ range: [-1, 1] }
)
Insert cell
data = d3.range(0, 1, 0.001).map((x) => ({ x, y: noiseloop.value(x) }))
Insert cell
function perlinImage(noise, options = {}) {
const { height = 400, width = 400 } = options;
const context = DOM.context2d(width, height, 1);
const image = context.createImageData(width, height);
for (let y = 0, i = 0; y < height; ++y) {
for (let x = 0; x < width; ++x, i += 4) {
image.data[i + 3] = (noise(x, y) + 0.5) * 256;
}
}
context.putImageData(image, 0, 0);
return context.canvas;
}
Insert cell
noise2 = function (options = {}) {
const { xScaleLog = -5, yScaleLog = -5, octaves = 3 } = options;
const xScale = 2 ** xScaleLog;
const yScale = 2 ** yScaleLog;
const noise = octave(perlin2, octaves);
return (x, y) => noise(x * xScale, y * yScale);
}
Insert cell
class NoiseLoop {
constructor(center, radius, noise, options = {}) {
const { domain = [0, 1], range = [0, 1] } = options;
Object.assign(this, { center, radius, noise, domain, range });
}
value(d) {
const angle =
((d - this.domain[0]) / (this.domain[1] - this.domain[0])) * Math.PI * 2;
const x = this.center[0] + this.radius * Math.cos(angle);
const y = this.center[1] + this.radius * Math.sin(angle);
return (
(this.noise(x, y) + 0.5) * (this.range[1] - this.range[0]) + this.range[0]
);
}
}
Insert cell
mutable sampleCircle = new Circle([200, 200], 50)
Insert cell
class Circle {
constructor(center, radius) {
this._center = [...center];
this._borderPoint = [center[0] + radius, center[1]];
}
set borderPoint(p) {
this._borderPoint = [...p];
}
get borderPoint() {
return [...this._borderPoint];
}
set center(p) {
let v = vec2.sub([], p, this._center);
this._center = [...p];
vec2.add(this._borderPoint, this._borderPoint, v);
}
get center() {
return [...this._center];
}
get radius() {
return vec2.dist(this._borderPoint, this._center);
}
draw(ctx, pointRadius = 4) {
ctx.beginPath();
ctx.arc(...this._center, this.radius, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
ctx.arc(...this._center, pointRadius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(...this._borderPoint, pointRadius, 0, Math.PI * 2);
ctx.fill();
}
}
Insert cell
import { perlin2, perlin3, octave } from "@mbostock/perlin-noise"
Insert cell
import { vec2 } from "@esperanc/vec2-utils"
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