Published
Edited
Jul 30, 2019
1 fork
Importers
14 stars
Insert cell
Insert cell
Insert cell
run(presets[preset])
Insert cell
presets = ({
'blobs': {
height: 500,
resolution: 1/3,
noiseScale: .025,
layers: 5,
lower: .9,
upper: .7,
tx: 0,
ty: .3,
tz: .3,
sxy: 120,
sz: -30,
colorLow: 'hsla(90,20%,20%,.3)',
colorHigh: 'hsla(50,90%,50%,1)',
gco: 'screen',
},
'web': {
resolution: 1/4,
noiseScale: .05,
layers: 7,
lower: .85,
upper: .95,
tx: .83,
ty: -.15,
tz: .23,
sxy: 80,
sz: -10,
colorLow: 'hsla(10,90%,30%,.9)',
colorHigh: 'hsla(30,90%,80%,.9)',
gco: 'screen',
},
'water': {
width: 500,
height: 500,
resolution: 1/2,
noiseScale: .020,
layers: 2,
lower: .985,
upper: .99,
tx: .15,
ty: -.5,
tz: .25,
sxy: 0,
sz: -40,
colorLow: 'hsla(200,100%,50%,1)',
colorHigh: 'hsla(200,100%,100%,.5)',
colorBg: 'hsl(200,100%,80%)',
gco: 'xor',
},
'default': {}
})
Insert cell
Insert cell
function* run(options = {}) {
const {
width: w = 800, // Output width
height: h = 300, // Output height
resolution: res = 1/4, // Layer resolution
noiseScale: s = .05, // Noise scale, resolution dependant
layers: count = 5, // Number of canvas layers
lower = 1, // Lower cutoff (inverted)
upper = 0, // Upper cutoff (inverted)
tx = 0, // Horizontal movement
ty = 0, // Vertical movement
tz = .1, // Depth movement
sxy = 0, // Perspective scaling
sz = 0, // Depth range
colorLow = '#000', // Back layer color
colorHigh = '#fff', // Front layer color
colorBg = '#000', // Background color
gco = 'source-over', // globalCompositeOperation
seed = '0' // random seed, '' for none
} = options;
const simplex = new noise('0');
const ws = w*res|0, hs = h*res|0;
const color = d3.interpolateHsl(colorLow, colorHigh);
const layers = Array(count).fill()
.map((v,i,a)=>i/(a.length-1))
.map(t => layer(ws, hs, color2rgba(color(t))));
const cOut = html`<canvas width=${w} height=${h} style="max-width:100%">`.getContext('2d');
cOut.globalCompositeOperation = gco;
const bgFill = new ImageData(w, h);
{
const bg = color2rgba(colorBg);
for(let i=0; i<bgFill.data.length; i+=4) bgFill.data.set(bg, i);
}
let t = 0;
while(true) {
cOut.putImageData(bgFill, 0, 0);
layers.map((l,i) => {
const lt = i/layers.length;
const u = (x, y) => smoothstep(
lower, upper,
1 - simplex.noise3D(x*s + t*tx, y*s + t*ty, lt*s*sz + t*tz) ** 2
);
const p = (lt ** 2) * sxy;
cOut.drawImage(l.update(u), 0, 0, ws, hs, -p, -p, w+p*2, h+p*2);
});
yield cOut.canvas;
t += 1/30;
}
}
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