Public
Edited
May 16, 2024
Insert cell
Insert cell
bz(DOM.context2d(W, W))
Insert cell
Insert cell
Insert cell
Insert cell
bz = (context) => {
// https://gist.github.com/bellbind/c805785a0c67cc5c108b17fc68f8a214
render(context, channels);
loop(context, channels);
return context.canvas;
}
Insert cell
function loop(context, channels) {
render(context, channels);
requestAnimationFrame(_ => loop(context, next(channels)({alpha, beta, gamma})));
}
Insert cell
channels = ({
A: randoms(w),
B: randoms(w),
C: randoms(w),
})
Insert cell
randoms = (w) => Array.from({length: w * w}, Math.random)
Insert cell
Insert cell
a = ([a, b, c]) => f(a,b,c, alpha, gamma)
Insert cell
b = ([a, b, c]) => f(b,c, a, beta, alpha)
Insert cell
c = ([a, b, c]) => f(b,c,a, gamma, beta)
Insert cell
f = (a,b,c, f, g) => clamp(a * (1 + f * b - g * c), 0, 1)
Insert cell
Insert cell
// idea logbook
when = ({
sparked: new Date("2024-05-14"), // when did the idead spark with me?
initial: new Date("2024-05-16"), // when did I first implement it?
})
Insert cell
colors = d3.schemeCategory10
Insert cell
W = w * s
Insert cell
w = 640 / 4
Insert cell
s = 1
Insert cell
nr = 2
Insert cell
F = [
...zip(
channels.A.map(convF),
channels.B.map(convF),
channels.B.map(convF),
)
]
Insert cell
// zap
// (
[
channels.A.map(convF),
channels.B.map(convF),
channels.B.map(convF),
] // )
Insert cell
({
A: F.map(([a, b, c]) => clamp(a * (1 + alpha * b - gamma * c), 0, 1)),
B: F.map(([a, b, c]) => clamp(b * (1 + beta * c - alpha * a), 0, 1)),
C: F.map(([a, b, c]) => clamp(c * (1 + gamma * a - beta * b), 0, 1)),
})
Insert cell
function* zip(...iterables) {
const itors = iterables.map(iterable => iterable[Symbol.iterator]());
for (
let curs = itors.map(itor => itor.next());
!curs.some(c => c.done);
curs = itors.map(itor => itor.next())
)
yield curs.map(c => c.value);
}
Insert cell
function zap(...iterables) {
const itors = iterables.map(iterable => iterable[Symbol.iterator]());
for (
let curs = itors.map(itor => itor.next());
!curs.some(c => c.done);
curs = itors.map(itor => itor.next())
)
return curs.map(c => c.value);
}
Insert cell
get = (cells, x, y) => cells[clamp(y, 0, w - 1) * w + clamp(x, 0, w - 1)]
Insert cell
conv = (f) => (v, i, cells) => {
const x = i % w, y = i / w | 0;
return f.reduce((s, c) => s + c.f * get(cells, x + c.x, y + c.y), 0);
}
Insert cell
force = (r) => {
const w = 2 * r + 1;
const conv = [...Array(w * w)]
.map(
(_, i) => {
const x = i % w - r, y = (i / w | 0) - r;
// f: square distance between cell centers as a force effect
const f = Math.max((r + 1 / 2) - Math.hypot(x, y), 0) ** 2;
return {x, y, f};
})
.filter(({f}) => f > 0);
const total = conv.reduce((s, {f}) => s + f, 0);
return conv.map(({x, y, f}) => ({x, y, f: f / total}));
}
Insert cell
convF = conv(force(nr));
Insert cell
render = (c2d, {A, B, C}) => {
c2d.clearRect(0, 0, c2d.canvas.width, c2d.canvas.height);
for (const [[a, b, c], i] of enumerate(zip(A, B, C))) {
const x = i % w, y = i / w | 0;
c2d.fillStyle = colors[argmax([a, b, c])];
c2d.fillRect(x * s, y * s, s, s);
}
}
Insert cell
clamp = (v, min, max) => Math.min(Math.max(min, v), max)
Insert cell
argmax = (a) => a.reduce((r, v, i) => v > r.v ? {v, i} : r, {v: a[0], i: 0}).i
Insert cell
function* enumerate(iterable) {
const itor = iterable[Symbol.iterator]();
for (let i = 0, c = itor.next(); !c.done;
i++, c = itor.next()) yield [c.value, i];
}
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