Public
Edited
Sep 11, 2023
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function drawLayers(data, { width = 400, strokeWidth = 0.75 } = {}) {
return Plot.plot({
width,
y: { domain: [-1, 1] },
x: {
axis: false,
grid: true
},
facet: {
// marginRight: 0
},
fy: {
padding: 0.125,
label: "Layer",
reverse: true
},
marks: [
Plot.frame({ stroke: "lightgray" }),
Plot.line(data, {
x: "x",
y: "value",
fy: "layer",
strokeWidth
})
]
});
}
Insert cell
function drawMergedLayer(data, { width = 400, strokeWidth = 0.75 } = {}) {
return Plot.plot({
width,
height: width / 2,
y: { domain: [-1, 1] },
x: {
axis: false,
grid: true
},
marks: [
Plot.frame({ stroke: "lightgray" }),
Plot.line(data, {
x: "x",
y: "value",
strokeWidth
})
]
});
}
Insert cell
function isNoisePreview() {
return previewWave === "Noise";
}
Insert cell
layers = {
const scale = 2 ** noiseScale1;
const fn = isNoisePreview() ? noise2DA : Math.sin;
return Array.from({ length: width }).flatMap((_, i) => {
const x = i * scale;

let totalAmplitude = 0;
let frequency = 1;
let amplitude = 1;
const layers = Array.from({ length: octaves1 + 1 }).map((_, l) => {
const value = fn(x * frequency, 0) * amplitude;

totalAmplitude += amplitude;
amplitude *= 0.5; // Halves amplitude in each layers
frequency *= 2; // Doubles the amplitude in each layers
return {
layer: l,
x: i,
value
};
});

return layers.map((obj) => ({ ...obj, totalAmplitude }));
});
}
Insert cell
layersMerged = d3
.groups(layers, (d) => d.x)
.map(([_, layers]) => {
const { totalAmplitude } = layers[0];
const value = sumBy(layers, (d) => d.value) / totalAmplitude;
return {
...layers[0],
value
};
})
Insert cell
noise2DA = {
const prng = alea(randomize1);
return simplex.createNoise2D(prng);
}
Insert cell
Insert cell
scale = 2 ** noiseScale
Insert cell
function drawNoise(
context,
{ octaves = 1, width, height, scale = 2 ** -5 } = {}
) {
const aspectRatio = height / width;

const image = context.createImageData(width * dpr, height * dpr);

const imageDataWidth = width * dpr * 4;
for (let i = 0; i < image.data.length; i += 4) {
const x = (i % imageDataWidth) / 4;
const y = Math.floor(i / imageDataWidth / 2);

const xScale = scale;
const yScale = scale / aspectRatio;
const noiseFn = octave(noise2DB, octaves + 1);
const noise = noiseFn(x * xScale, y * yScale);
const u = 0.5 + noise * 0.5;
const v = Math.trunc(math.lerp(0, 255, u));

image.data[i + 0] = v; // R
image.data[i + 1] = v; // G
image.data[i + 2] = v; // B
image.data[i + 3] = 255; // Alpha
}

context.putImageData(image, 0, 0);
}
Insert cell
// Based on Mike Bostock's https://observablehq.com/@mbostock/perlin-noise#octave
function octave(noiseFn, octaves) {
return function (x, y) {
let total = 0;
let frequency = 1;
let amplitude = 1;
let value = 0;

for (let i = 0; i < octaves; ++i) {
value += noiseFn(x * frequency, y * frequency) * amplitude;

total += amplitude;
amplitude *= 0.5;
frequency *= 2;
}

return value / total;
};
}
Insert cell
dpr = window.devicePixelRatio
Insert cell
aspectRatio = 9 / 16
Insert cell
height = Math.floor(width * aspectRatio)
Insert cell
noise2DB = {
const prng = alea(randomize);
return simplex.createNoise2D(prng);
}
Insert cell
Insert cell
// Source: https://youmightnotneed.com/lodash/#sumBy
sumBy = (arr, func) => arr.reduce((acc, item) => acc + func(item), 0)
Insert cell
Insert cell
<style>
.grid {
display: grid;
grid-template-columns: 8fr 1fr 8fr;
align-items: center;
}
.symbol {
font-size: 4rem;
font-weight: bold;
color: var(--lt-color-gray-500, #999);
}
</style>
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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