Public
Edited
May 10, 2024
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
text = 'hello'
Insert cell
Insert cell
renderables = {
let grid = []
let cols = Array(numCols).fill().map(() => Array(numRows).fill(0))
let colWidths = Array(numCols).fill(0)
const ctx = document.createElement('canvas').getContext('2d')
ctx.canvas.width = w
ctx.canvas.height = h

const image = await FileAttachment("IMG_8196.jpeg").image();
const ocanv = new OffscreenCanvas(w, h)
const octx = ocanv.getContext('2d', { willReadFrequently: true })
octx.drawImage(image, 0, 0, w, h)

octx.fillStyle = '#fff'
octx.font = 'bold 400px sans-serif'
octx.textAlign = 'center'
octx.textBaseline = 'middle'
octx.fillText(text, w / 2, h / 2, w)

// read image, init grid
for (let i = 0; i < numCols; i++) {
const x = i * gridSize + gridSize / 2;
for (let j = 0; j < numRows; j++) {
const y = j * gridSize + gridSize / 2;
if (octx.getImageData(x | 0, y | 0, 1, 1).data[3] > 0) {
ctx.fillRect(x - size / 2, y - size / 2, size, size)
grid.push(1)
} else {
grid.push(0)
}
}
}
ctx.clearRect(0, 0, w, h)

let colWidthSum = 0

const getRenderFn = () => {
let elapsed = 0
let prev = 0
return t => {
elapsed = t - prev;
prev = t;

colWidthSum = 0;
const now = Date.now();

// const dynamicVar = Date.now() * 0.0005;
const dynamicVar = t * 0.0002;

let maxCol = 0
let maxCell = 0
for (let i = 0; i < numCols; i++) {
const nx = exp ? i * 0.5 : i / numCols
const colWidth = util.pos(noise2d(nx, dynamicVar)) // * 0.001
colWidths[i] = colWidth
colWidthSum += colWidth
if (colWidth > maxCol) maxCol = colWidth

// var: # subdivisions
let colSum = 0;
for (let j = 0; j < numRows; j++) {
const ny = exp ? j * 0.5 : j / numRows
// vars: min size, max size
const value = util.pos(noise3d(nx, ny, dynamicVar)) // * 0.001
cols[i][j] = value;
colSum += value
}
for (let j = 0; j < numRows; j++) {
cols[i][j] /= colSum
if (cols[i][j] > maxCell) maxCell = cols[i][j];
}
}
for (let i = 0; i < numCols; i++) {
colWidths[i] /= colWidthSum
if (colWidths[i] > maxCol) maxCol = colWidths[i]
}
// draw
const sWidth = 1 / numCols * w
const sHeight = 1 / numRows * h
let colSum = 0
for (let i = 0; i < numCols; i++) {
const x = colSum * w
const sx = i / numCols * w
const dWidth = colWidths[i] * w
colSum += colWidths[i]
let rowSum = 0
for (let j = 0; j < numRows; j++) {
const y = rowSum * h
const sy = j / numRows * h
const dHeight = cols[i][j] * h
ctx.drawImage(ocanv, sx, sy, sWidth, sHeight, x, y, dWidth, dHeight)
rowSum += cols[i][j]
}
}
}
}

return { ctx, render: getRenderFn() }
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
util = {
const rand = (max = 1) => Math.random() * max;

const flip = (a, b, threshold = 0.5) => rand() < threshold ? a : b;
const isNil = x => x === undefined || x === null;
const sum = (arr, start = 0, end = arr.length, sum = 0) => {
for (let i = start; i < end; i++) sum += arr[i];
return sum;
}

// index to xy
const ixy = (idx, width, dst = []) => (
dst[0] = idx % width,
dst[1] = Math.floor(idx / width),
dst
);

const ixyj = (idx, width, dst = []) => (
ixy(idx / 4, width, dst),
dst[3] = idx / 4,
dst
);

const clamp = (x, min, max) => Math.min(Math.max(x, min), max);

// -1, 1 to 0, 1:
const pos = n => (n + 1) / 2;
const mix = (min, max, a) => (max - min) * a + min;

return {
rand,
flip,
clamp,
mix,
pos,
isNil,
sum,
ixy,
ixyj
}
}
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