Published
Edited
Jan 21, 2022
1 star
Insert cell
Insert cell
Insert cell
jitterFn = i => (
Math.sin(i / 137) * image.naturalWidth * 1 / 2 +
Math.sin(i / 11) * image.naturalWidth * 1 / 3 +
Math.sin(1 / 2) * image.naturalWidth * 1 / 8
)
Insert cell
Insert cell
image = FileAttachment("maine.jpeg").image()
Insert cell
imageData = {
const canvas = DOM.canvas(image.naturalWidth, image.naturalHeight)
canvas.style.maxWidth = '100%'
const ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0)
return ctx.getImageData(0, 0, image.naturalWidth, image.naturalHeight)
}
Insert cell
BAND_HEIGHT = image.naturalHeight / 100
Insert cell
chunked = chunkify(imageData.data, image.naturalWidth * 4).map(row => chunkify(row, 4))
Insert cell
shifted = chunked.map(
(row, i) => shift(row, jitterFn(i))
)
Insert cell
flattened = new Uint8ClampedArray(shifted.flat().reduce((acc, curr) => { acc.push(...curr); return acc }, []))
Insert cell
Insert cell
chunkify = (arr, chunkLength) => {
const chunkCount = Math.ceil(arr.length / chunkLength)
return new Array(chunkCount).fill().map((_, i) => arr.slice(i * chunkLength, (i + 1) * chunkLength))
}
Insert cell
shift = (arr, shiftAmount, direction = 'LEFT') => {
if (direction === 'LEFT') {
return arr.slice(shiftAmount).concat(arr.slice(0, shiftAmount))
} else {
return arr.slice(-shiftAmount).concat(arr.slice(0, arr.length - shiftAmount))
}
}
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