Published
Edited
Apr 15, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
ruleTable = Array(stateCount * 10)
.fill()
.map(() => Math.floor(Math.random() * stateCount))
Insert cell
updateOnTick = 1
Insert cell
tick = {
let tick = 0;
while (true) {
yield tick++;
}
}
Insert cell
stateCount = 3
Insert cell
commandRender = regl({
frag: `
precision mediump float;
const vec2 size = vec2(${canvas.width}, ${canvas.height});
const vec2 invSize = vec2(
${(1 / canvas.width).toPrecision(5)},
${(1 / canvas.height).toPrecision(5)});

const int radius = ${radius};
const float invRadius = ${(1 / radius).toPrecision(5)};
uniform sampler2D space;
varying vec2 uv;


vec3 getSimplexCellCoords(vec2 coords) {
const float c1 = 0.366025403784439; // 0.5*(sqrt(3.0)-1.0)
const float c2 = 0.211324865405187; // (3.0-sqrt(3.0))/6.0

vec2 doublecellPosition = floor(coords + dot(coords, vec2(c1)) );
vec2 x0 = coords - doublecellPosition + dot(doublecellPosition, vec2(c2));

return vec3(doublecellPosition, x0.x > x0.y ? 1 : -1);
}
void main() {
vec3 simplexCellCoords = getSimplexCellCoords(uv * size);
vec4 doublecell = texture2D(space, simplexCellCoords.xy * invRadius);
float cell = (simplexCellCoords.z <= 0.0) ? doublecell.x : doublecell.y;

if (int(cell * 3.0) == 2) {
gl_FragColor = vec4(1, 0, 0, 1);
} else if (int(cell * 3.0) == 1) {
gl_FragColor = vec4(0, 1, 0, 1);
} else {
gl_FragColor = vec4(0, 0, 1, 1);
}
}`,

vert: `
precision mediump float;
attribute vec2 position;
varying vec2 uv;
uniform mat3 zoomMatrix;
void main() {
vec2 zoomedPosition = (zoomMatrix * vec3(position, 1)).xy;
uv = 0.5 * (zoomedPosition + 1.0);
gl_Position = vec4(position, 0, 1);
}`,
uniforms: {
space: (_, { space }) => space,
zoomMatrix: (_, { zoomMatrix }) => zoomMatrix
},
attributes: { position: [-3, -1, 3, -1, 0, 2] },
count: 3,
depth: { enable: false }
})
Insert cell
ruleTableCode = ruleTable
.map((r, i) => `if (combinedState == ${i}) { return ${r}; }`)
.join(`\n`)
Insert cell
commandUpdate = regl({
frag: `
precision mediump float;
uniform sampler2D space;
const int radius = ${radius};
const float invRadius = ${(1 / radius).toPrecision(5)};
varying vec2 uv;
varying vec2 cellPosition;
const int ruleTableLength = 2 * 4;
int getSymmetryState(int n1, int n2, int n3) {
if (n1 == 0 && n2 == 0 && n3 == 0) { return 0; }
if (n1 == 0 && n2 == 0 && n3 == 1) { return 1; }
if (n1 == 0 && n2 == 0 && n3 == 2) { return 4; }
if (n1 == 0 && n2 == 1 && n3 == 0) { return 1; }
if (n1 == 0 && n2 == 1 && n3 == 1) { return 2; }
if (n1 == 0 && n2 == 1 && n3 == 2) { return 5; }
if (n1 == 0 && n2 == 2 && n3 == 0) { return 4; }
if (n1 == 0 && n2 == 2 && n3 == 1) { return 5; }
if (n1 == 0 && n2 == 2 && n3 == 2) { return 6; }
if (n1 == 1 && n2 == 0 && n3 == 0) { return 1; }
if (n1 == 1 && n2 == 0 && n3 == 1) { return 2; }
if (n1 == 1 && n2 == 0 && n3 == 2) { return 5; }
if (n1 == 1 && n2 == 1 && n3 == 0) { return 2; }
if (n1 == 1 && n2 == 1 && n3 == 1) { return 3; }
if (n1 == 1 && n2 == 1 && n3 == 2) { return 7; }
if (n1 == 1 && n2 == 2 && n3 == 0) { return 5; }
if (n1 == 1 && n2 == 2 && n3 == 1) { return 7; }
if (n1 == 1 && n2 == 2 && n3 == 2) { return 8; }
if (n1 == 2 && n2 == 0 && n3 == 0) { return 4; }
if (n1 == 2 && n2 == 0 && n3 == 1) { return 5; }
if (n1 == 2 && n2 == 0 && n3 == 2) { return 6; }
if (n1 == 2 && n2 == 1 && n3 == 0) { return 5; }
if (n1 == 2 && n2 == 1 && n3 == 1) { return 7; }
if (n1 == 2 && n2 == 1 && n3 == 2) { return 8; }
if (n1 == 2 && n2 == 2 && n3 == 0) { return 6; }
if (n1 == 2 && n2 == 2 && n3 == 1) { return 8; }
if (n1 == 2 && n2 == 2 && n3 == 2) { return 9; }
}
int getNextState(int c, int n1, int n2, int n3) {
int n = getSymmetryState(n1, n2, n3);
int stateCount = ${stateCount};
int combinedState = n * stateCount + c;
${ruleTableCode}
}

void main() {
int nextUpperCell = getNextState(
int(3.0 * texture2D(space, cellPosition * invRadius).g),
int(3.0 * texture2D(space, (cellPosition + vec2(1.0, 0.0)) * invRadius).r),
int(3.0 * texture2D(space, (cellPosition + vec2(0.0, -1.0)) * invRadius).r),
int(3.0 * texture2D(space, cellPosition * invRadius).r));

int nextLowerCell = getNextState(
int(3.0 * texture2D(space, cellPosition * invRadius).r),
int(3.0 * texture2D(space, (cellPosition + vec2(-1.0, 0.0)) * invRadius).g),
int(3.0 * texture2D(space, (cellPosition + vec2(0.0, 1.0)) * invRadius).g),
int(3.0 * texture2D(space, cellPosition * invRadius).g));

gl_FragColor = vec4(float(nextLowerCell) / 3.0, float(nextUpperCell) / 3.0, 0.0, 0.0);
}`,

vert: `
precision mediump float;
const int radius = ${radius};
const float invRadius = ${(1 / radius).toPrecision(5)};
attribute vec2 position;
varying vec2 uv;
varying vec2 cellPosition;
void main() {
uv = 0.5 * (position + 1.0);
cellPosition = uv * float(radius);
gl_Position = vec4(position, 0, 1);
}`,
uniforms: {
space: (_, { space }) => space
},
framebuffer: (_, { nextSpace }) => nextSpace,
attributes: { position: [-3, -1, 3, -1, 0, 2] },
count: 3,
depth: { enable: false }
})
Insert cell
mutable state = (reset,
Array(2)
.fill()
.map(() =>
regl.framebuffer({
color: regl.texture({
radius,
data: initialState,
wrap: 'repeat'
}),
depth: false,
depthStencil: false
})
))
Insert cell
initialState = Array(radius * radius * 4)
.fill()
.map(
(_, i) =>
((Math.random() > 0.8 ? (Math.random() > 0.8 ? 2 : 1) : 0) / 3) * 255
)
Insert cell
Insert cell
updater = {
step;
if (tick % updateOnTick != 0) {
return;
}
commandUpdate(
{
nextSpace: mutable state[0],
space: mutable state[1]
},
() => {
regl.draw();
mutable state = [mutable state[1], mutable state[0]];
}
);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more